home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkTextBTree.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  101.5 KB  |  3,595 lines

  1. /* 
  2.  * tkTextBTree.c --
  3.  *
  4.  *    This file contains code that manages the B-tree representation
  5.  *    of text for Tk's text widget and implements character and
  6.  *    toggle segment types.
  7.  *
  8.  * Copyright (c) 1992-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkTextBTree.c 1.35 96/03/21 15:51:39
  15.  */
  16.  
  17. #include "tkInt.h"
  18. #include "tkPort.h"
  19. #include "tkText.h"
  20.  
  21. /*
  22.  * The data structure below keeps summary information about one tag as part
  23.  * of the tag information in a node.
  24.  */
  25.  
  26. typedef struct Summary {
  27.     TkTextTag *tagPtr;            /* Handle for tag. */
  28.     int toggleCount;            /* Number of transitions into or
  29.                      * out of this tag that occur in
  30.                      * the subtree rooted at this node. */
  31.     struct Summary *nextPtr;        /* Next in list of all tags for same
  32.                      * node, or NULL if at end of list. */
  33. } Summary;
  34.  
  35. /*
  36.  * The data structure below defines a node in the B-tree.
  37.  */
  38.  
  39. typedef struct Node {
  40.     struct Node *parentPtr;        /* Pointer to parent node, or NULL if
  41.                      * this is the root. */
  42.     struct Node *nextPtr;        /* Next in list of siblings with the
  43.                      * same parent node, or NULL for end
  44.                      * of list. */
  45.     Summary *summaryPtr;        /* First in malloc-ed list of info
  46.                      * about tags in this subtree (NULL if
  47.                      * no tag info in the subtree). */
  48.     int level;                /* Level of this node in the B-tree.
  49.                      * 0 refers to the bottom of the tree
  50.                      * (children are lines, not nodes). */
  51.     union {                /* First in linked list of children. */
  52.     struct Node *nodePtr;        /* Used if level > 0. */
  53.     TkTextLine *linePtr;        /* Used if level == 0. */
  54.     } children;
  55.     int numChildren;            /* Number of children of this node. */
  56.     int numLines;            /* Total number of lines (leaves) in
  57.                      * the subtree rooted here. */
  58. } Node;
  59.  
  60. /*
  61.  * Upper and lower bounds on how many children a node may have:
  62.  * rebalance when either of these limits is exceeded.  MAX_CHILDREN
  63.  * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
  64.  */
  65.  
  66. #define MAX_CHILDREN 12
  67. #define MIN_CHILDREN 6
  68.  
  69. /*
  70.  * The data structure below defines an entire B-tree.
  71.  */
  72.  
  73. typedef struct BTree {
  74.     Node *rootPtr;            /* Pointer to root of B-tree. */
  75.     TkText *textPtr;            /* Used to find tagTable in consistency
  76.                      * checking code */
  77. } BTree;
  78.  
  79. /*
  80.  * The structure below is used to pass information between
  81.  * TkBTreeGetTags and IncCount:
  82.  */
  83.  
  84. typedef struct TagInfo {
  85.     int numTags;            /* Number of tags for which there
  86.                      * is currently information in
  87.                      * tags and counts. */
  88.     int arraySize;            /* Number of entries allocated for
  89.                      * tags and counts. */
  90.     TkTextTag **tagPtrs;        /* Array of tags seen so far.
  91.                      * Malloc-ed. */
  92.     int *counts;            /* Toggle count (so far) for each
  93.                      * entry in tags.  Malloc-ed. */
  94. } TagInfo;
  95.  
  96. /*
  97.  * Variable that indicates whether to enable consistency checks for
  98.  * debugging.
  99.  */
  100.  
  101. int tkBTreeDebug = 0;
  102.  
  103. /*
  104.  * Macros that determine how much space to allocate for new segments:
  105.  */
  106.  
  107. #define CSEG_SIZE(chars) ((unsigned) (Tk_Offset(TkTextSegment, body) \
  108.     + 1 + (chars)))
  109. #define TSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
  110.     + sizeof(TkTextToggle)))
  111.  
  112. /*
  113.  * Forward declarations for procedures defined in this file:
  114.  */
  115.  
  116. static void        ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
  117.                 TkTextTag *tagPtr, int delta));
  118. static void        CharCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  119.                 TkTextLine *linePtr));
  120. static int        CharDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  121.                 TkTextLine *linePtr, int treeGone));
  122. static TkTextSegment *    CharCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  123.                 TkTextLine *linePtr));
  124. static TkTextSegment *    CharSplitProc _ANSI_ARGS_((TkTextSegment *segPtr,
  125.                 int index));
  126. static void        CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
  127. static void        CleanupLine _ANSI_ARGS_((TkTextLine *linePtr));
  128. static void        DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
  129. static void        DestroyNode _ANSI_ARGS_((Node *nodePtr));
  130. static void        IncCount _ANSI_ARGS_((TkTextTag *tagPtr, int inc,
  131.                 TagInfo *tagInfoPtr));
  132. static void        Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
  133. static void        RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
  134. static TkTextSegment *    SplitSeg _ANSI_ARGS_((TkTextIndex *indexPtr));
  135. static void        ToggleCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  136.                 TkTextLine *linePtr));
  137. static TkTextSegment *    ToggleCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  138.                 TkTextLine *linePtr));
  139. static int        ToggleDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  140.                 TkTextLine *linePtr, int treeGone));
  141. static void        ToggleLineChangeProc _ANSI_ARGS_((TkTextSegment *segPtr,
  142.                 TkTextLine *linePtr));
  143. static TkTextSegment *    FindTagStart _ANSI_ARGS_((TkTextBTree tree,
  144.                 TkTextTag *tagPtr, TkTextIndex *indexPtr));
  145.  
  146. /*
  147.  * Type record for character segments:
  148.  */
  149.  
  150. Tk_SegType tkTextCharType = {
  151.     "character",                /* name */
  152.     0,                        /* leftGravity */
  153.     CharSplitProc,                /* splitProc */
  154.     CharDeleteProc,                /* deleteProc */
  155.     CharCleanupProc,                /* cleanupProc */
  156.     (Tk_SegLineChangeProc *) NULL,        /* lineChangeProc */
  157.     TkTextCharLayoutProc,            /* layoutProc */
  158.     CharCheckProc                /* checkProc */
  159. };
  160.  
  161. /*
  162.  * Type record for segments marking the beginning of a tagged
  163.  * range:
  164.  */
  165.  
  166. Tk_SegType tkTextToggleOnType = {
  167.     "toggleOn",                    /* name */
  168.     0,                        /* leftGravity */
  169.     (Tk_SegSplitProc *) NULL,            /* splitProc */
  170.     ToggleDeleteProc,                /* deleteProc */
  171.     ToggleCleanupProc,                /* cleanupProc */
  172.     ToggleLineChangeProc,            /* lineChangeProc */
  173.     (Tk_SegLayoutProc *) NULL,            /* layoutProc */
  174.     ToggleCheckProc                /* checkProc */
  175. };
  176.  
  177. /*
  178.  * Type record for segments marking the end of a tagged
  179.  * range:
  180.  */
  181.  
  182. Tk_SegType tkTextToggleOffType = {
  183.     "toggleOff",                /* name */
  184.     1,                        /* leftGravity */
  185.     (Tk_SegSplitProc *) NULL,            /* splitProc */
  186.     ToggleDeleteProc,                /* deleteProc */
  187.     ToggleCleanupProc,                /* cleanupProc */
  188.     ToggleLineChangeProc,            /* lineChangeProc */
  189.     (Tk_SegLayoutProc *) NULL,            /* layoutProc */
  190.     ToggleCheckProc                /* checkProc */
  191. };
  192.  
  193. /*
  194.  *----------------------------------------------------------------------
  195.  *
  196.  * TkBTreeCreate --
  197.  *
  198.  *    This procedure is called to create a new text B-tree.
  199.  *
  200.  * Results:
  201.  *    The return value is a pointer to a new B-tree containing
  202.  *    one line with nothing but a newline character.
  203.  *
  204.  * Side effects:
  205.  *    Memory is allocated and initialized.
  206.  *
  207.  *----------------------------------------------------------------------
  208.  */
  209.  
  210. TkTextBTree
  211. TkBTreeCreate(textPtr)
  212.     TkText *textPtr;
  213. {
  214.     register BTree *treePtr;
  215.     register Node *rootPtr;
  216.     register TkTextLine *linePtr, *linePtr2;
  217.     register TkTextSegment *segPtr;
  218.  
  219.     /*
  220.      * The tree will initially have two empty lines.  The second line
  221.      * isn't actually part of the tree's contents, but its presence
  222.      * makes several operations easier.  The tree will have one node,
  223.      * which is also the root of the tree.
  224.      */
  225.  
  226.     rootPtr = (Node *) ckalloc(sizeof(Node));
  227.     linePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  228.     linePtr2 = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  229.     rootPtr->parentPtr = NULL;
  230.     rootPtr->nextPtr = NULL;
  231.     rootPtr->summaryPtr = NULL;
  232.     rootPtr->level = 0;
  233.     rootPtr->children.linePtr = linePtr;
  234.     rootPtr->numChildren = 2;
  235.     rootPtr->numLines = 2;
  236.  
  237.     linePtr->parentPtr = rootPtr;
  238.     linePtr->nextPtr = linePtr2;
  239.     segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
  240.     linePtr->segPtr = segPtr;
  241.     segPtr->typePtr = &tkTextCharType;
  242.     segPtr->nextPtr = NULL;
  243.     segPtr->size = 1;
  244.     segPtr->body.chars[0] = '\n';
  245.     segPtr->body.chars[1] = 0;
  246.  
  247.     linePtr2->parentPtr = rootPtr;
  248.     linePtr2->nextPtr = NULL;
  249.     segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
  250.     linePtr2->segPtr = segPtr;
  251.     segPtr->typePtr = &tkTextCharType;
  252.     segPtr->nextPtr = NULL;
  253.     segPtr->size = 1;
  254.     segPtr->body.chars[0] = '\n';
  255.     segPtr->body.chars[1] = 0;
  256.  
  257.     treePtr = (BTree *) ckalloc(sizeof(BTree));
  258.     treePtr->rootPtr = rootPtr;
  259.     treePtr->textPtr = textPtr;
  260.  
  261.     return (TkTextBTree) treePtr;
  262. }
  263.  
  264. /*
  265.  *----------------------------------------------------------------------
  266.  *
  267.  * TkBTreeDestroy --
  268.  *
  269.  *    Delete a B-tree, recycling all of the storage it contains.
  270.  *
  271.  * Results:
  272.  *    The tree given by treePtr is deleted.  TreePtr should never
  273.  *    again be used.
  274.  *
  275.  * Side effects:
  276.  *    Memory is freed.
  277.  *
  278.  *----------------------------------------------------------------------
  279.  */
  280.  
  281. void
  282. TkBTreeDestroy(tree)
  283.     TkTextBTree tree;            /* Pointer to tree to delete. */ 
  284. {
  285.     BTree *treePtr = (BTree *) tree;
  286.  
  287.     DestroyNode(treePtr->rootPtr);
  288.     ckfree((char *) treePtr);
  289. }
  290.  
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  * DestroyNode --
  295.  *
  296.  *    This is a recursive utility procedure used during the deletion
  297.  *    of a B-tree.
  298.  *
  299.  * Results:
  300.  *    None.
  301.  *
  302.  * Side effects:
  303.  *    All the storage for nodePtr and its descendants is freed.
  304.  *
  305.  *----------------------------------------------------------------------
  306.  */
  307.  
  308. static void
  309. DestroyNode(nodePtr)
  310.     register Node *nodePtr;
  311. {
  312.     if (nodePtr->level == 0) {
  313.     TkTextLine *linePtr;
  314.     TkTextSegment *segPtr;
  315.  
  316.     while (nodePtr->children.linePtr != NULL) {
  317.         linePtr = nodePtr->children.linePtr;
  318.         nodePtr->children.linePtr = linePtr->nextPtr;
  319.         while (linePtr->segPtr != NULL) {
  320.         segPtr = linePtr->segPtr;
  321.         linePtr->segPtr = segPtr->nextPtr;
  322.         (*segPtr->typePtr->deleteProc)(segPtr, linePtr, 1);
  323.         }
  324.         ckfree((char *) linePtr);
  325.     }
  326.     } else {
  327.     register Node *childPtr;
  328.  
  329.     while (nodePtr->children.nodePtr != NULL) {
  330.         childPtr = nodePtr->children.nodePtr;
  331.         nodePtr->children.nodePtr = childPtr->nextPtr;
  332.         DestroyNode(childPtr);
  333.     }
  334.     }
  335.     DeleteSummaries(nodePtr->summaryPtr);
  336.     ckfree((char *) nodePtr);
  337. }
  338.  
  339. /*
  340.  *----------------------------------------------------------------------
  341.  *
  342.  * DeleteSummaries --
  343.  *
  344.  *    Free up all of the memory in a list of tag summaries associated
  345.  *    with a node.
  346.  *
  347.  * Results:
  348.  *    None.
  349.  *
  350.  * Side effects:
  351.  *    Storage is released.
  352.  *
  353.  *----------------------------------------------------------------------
  354.  */
  355.  
  356. static void
  357. DeleteSummaries(summaryPtr)
  358.     register Summary *summaryPtr;    /* First in list of node's tag
  359.                      * summaries. */
  360. {
  361.     register Summary *nextPtr;
  362.     while (summaryPtr != NULL) {
  363.     nextPtr = summaryPtr->nextPtr;
  364.     ckfree((char *) summaryPtr);
  365.     summaryPtr = nextPtr;
  366.     }
  367. }
  368.  
  369. /*
  370.  *----------------------------------------------------------------------
  371.  *
  372.  * TkBTreeInsertChars --
  373.  *
  374.  *    Insert characters at a given position in a B-tree.
  375.  *
  376.  * Results:
  377.  *    None.
  378.  *
  379.  * Side effects:
  380.  *    Characters are added to the B-tree at the given position.
  381.  *    If the string contains newlines, new lines will be added,
  382.  *    which could cause the structure of the B-tree to change.
  383.  *
  384.  *----------------------------------------------------------------------
  385.  */
  386.  
  387. void
  388. TkBTreeInsertChars(indexPtr, string)
  389.     register TkTextIndex *indexPtr;    /* Indicates where to insert text.
  390.                      * When the procedure returns, this
  391.                      * index is no longer valid because
  392.                      * of changes to the segment
  393.                      * structure. */
  394.     char *string;            /* Pointer to bytes to insert (may
  395.                      * contain newlines, must be null-
  396.                      * terminated). */
  397. {
  398.     register Node *nodePtr;
  399.     register TkTextSegment *prevPtr;    /* The segment just before the first
  400.                      * new segment (NULL means new segment
  401.                      * is at beginning of line). */
  402.     TkTextSegment *curPtr;        /* Current segment;  new characters
  403.                      * are inserted just after this one. 
  404.                      * NULL means insert at beginning of
  405.                      * line. */
  406.     TkTextLine *linePtr;        /* Current line (new segments are
  407.                      * added to this line). */
  408.     register TkTextSegment *segPtr;
  409.     TkTextLine *newLinePtr;
  410.     int chunkSize;            /* # characters in current chunk. */
  411.     register char *eol;            /* Pointer to character just after last
  412.                      * one in current chunk. */
  413.     int changeToLineCount;        /* Counts change to total number of
  414.                      * lines in file. */
  415.  
  416.     prevPtr = SplitSeg(indexPtr);
  417.     linePtr = indexPtr->linePtr;
  418.     curPtr = prevPtr;
  419.  
  420.     /*
  421.      * Chop the string up into lines and create a new segment for
  422.      * each line, plus a new line for the leftovers from the
  423.      * previous line.
  424.      */
  425.  
  426.     changeToLineCount = 0;
  427.     while (*string != 0) {
  428.     for (eol = string; *eol != 0; eol++) {
  429.         if (*eol == '\n') {
  430.         eol++;
  431.         break;
  432.         }
  433.     }
  434.     chunkSize = eol-string;
  435.     segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(chunkSize));
  436.     segPtr->typePtr = &tkTextCharType;
  437.     if (curPtr == NULL) {
  438.         segPtr->nextPtr = linePtr->segPtr;
  439.         linePtr->segPtr = segPtr;
  440.     } else {
  441.         segPtr->nextPtr = curPtr->nextPtr;
  442.         curPtr->nextPtr = segPtr;
  443.     }
  444.     segPtr->size = chunkSize;
  445.     strncpy(segPtr->body.chars, string, (size_t) chunkSize);
  446.     segPtr->body.chars[chunkSize] = 0;
  447.     curPtr = segPtr;
  448.  
  449.     if (eol[-1] != '\n') {
  450.         break;
  451.     }
  452.  
  453.     /*
  454.      * The chunk ended with a newline, so create a new TkTextLine
  455.      * and move the remainder of the old line to it.
  456.      */
  457.  
  458.     newLinePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  459.     newLinePtr->parentPtr = linePtr->parentPtr;
  460.     newLinePtr->nextPtr = linePtr->nextPtr;
  461.     linePtr->nextPtr = newLinePtr;
  462.     newLinePtr->segPtr = segPtr->nextPtr;
  463.     segPtr->nextPtr = NULL;
  464.     linePtr = newLinePtr;
  465.     curPtr = NULL;
  466.     changeToLineCount++;
  467.  
  468.     string = eol;
  469.     }
  470.  
  471.     /*
  472.      * Cleanup the starting line for the insertion, plus the ending
  473.      * line if it's different.
  474.      */
  475.  
  476.     CleanupLine(indexPtr->linePtr);
  477.     if (linePtr != indexPtr->linePtr) {
  478.     CleanupLine(linePtr);
  479.     }
  480.  
  481.     /*
  482.      * Increment the line counts in all the parent nodes of the insertion
  483.      * point, then rebalance the tree if necessary.
  484.      */
  485.  
  486.     for (nodePtr = linePtr->parentPtr ; nodePtr != NULL;
  487.         nodePtr = nodePtr->parentPtr) {
  488.     nodePtr->numLines += changeToLineCount;
  489.     }
  490.     nodePtr = linePtr->parentPtr;
  491.     nodePtr->numChildren += changeToLineCount;
  492.     if (nodePtr->numChildren > MAX_CHILDREN) {
  493.     Rebalance((BTree *) indexPtr->tree, nodePtr);
  494.     }
  495.  
  496.     if (tkBTreeDebug) {
  497.     TkBTreeCheck(indexPtr->tree);
  498.     }
  499. }
  500.  
  501. /*
  502.  *--------------------------------------------------------------
  503.  *
  504.  * SplitSeg --
  505.  *
  506.  *    This procedure is called before adding or deleting
  507.  *    segments.  It does three things: (a) it finds the segment
  508.  *    containing indexPtr;  (b) if there are several such
  509.  *    segments (because some segments have zero length) then
  510.  *    it picks the first segment that does not have left
  511.  *    gravity;  (c) if the index refers to the middle of
  512.  *    a segment then it splits the segment so that the
  513.  *    index now refers to the beginning of a segment.
  514.  *
  515.  * Results:
  516.  *    The return value is a pointer to the segment just
  517.  *    before the segment corresponding to indexPtr (as
  518.  *    described above).  If the segment corresponding to
  519.  *    indexPtr is the first in its line then the return
  520.  *    value is NULL.
  521.  *
  522.  * Side effects:
  523.  *    The segment referred to by indexPtr is split unless
  524.  *    indexPtr refers to its first character.
  525.  *
  526.  *--------------------------------------------------------------
  527.  */
  528.  
  529. static TkTextSegment *
  530. SplitSeg(indexPtr)
  531.     TkTextIndex *indexPtr;        /* Index identifying position
  532.                      * at which to split a segment. */
  533. {
  534.     TkTextSegment *prevPtr, *segPtr;
  535.     int count;
  536.  
  537.     for (count = indexPtr->charIndex, prevPtr = NULL,
  538.         segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
  539.         count -= segPtr->size, prevPtr = segPtr, segPtr = segPtr->nextPtr) {
  540.     if (segPtr->size > count) {
  541.         if (count == 0) {
  542.         return prevPtr;
  543.         }
  544.         segPtr = (*segPtr->typePtr->splitProc)(segPtr, count);
  545.         if (prevPtr == NULL) {
  546.         indexPtr->linePtr->segPtr = segPtr;
  547.         } else {
  548.         prevPtr->nextPtr = segPtr;
  549.         }
  550.         return segPtr;
  551.     } else if ((segPtr->size == 0) && (count == 0)
  552.         && !segPtr->typePtr->leftGravity) {
  553.         return prevPtr;
  554.     }
  555.     }
  556.     panic("SplitSeg reached end of line!");
  557.     return NULL;
  558. }
  559.  
  560. /*
  561.  *--------------------------------------------------------------
  562.  *
  563.  * CleanupLine --
  564.  *
  565.  *    This procedure is called after modifications have been
  566.  *    made to a line.  It scans over all of the segments in
  567.  *    the line, giving each a chance to clean itself up, e.g.
  568.  *    by merging with the following segments, updating internal
  569.  *    information, etc.
  570.  *
  571.  * Results:
  572.  *    None.
  573.  *
  574.  * Side effects:
  575.  *    Depends on what the segment-specific cleanup procedures do.
  576.  *
  577.  *--------------------------------------------------------------
  578.  */
  579.  
  580. static void
  581. CleanupLine(linePtr)
  582.     TkTextLine *linePtr;        /* Line to be cleaned up. */
  583. {
  584.     TkTextSegment *segPtr, **prevPtrPtr;
  585.     int anyChanges;
  586.  
  587.     /*
  588.      * Make a pass over all of the segments in the line, giving each
  589.      * a chance to clean itself up.  This could potentially change
  590.      * the structure of the line, e.g. by merging two segments
  591.      * together or having two segments cancel themselves;  if so,
  592.      * then repeat the whole process again, since the first structure
  593.      * change might make other structure changes possible.  Repeat
  594.      * until eventually there are no changes.
  595.      */
  596.  
  597.     while (1) {
  598.     anyChanges = 0;
  599.     for (prevPtrPtr = &linePtr->segPtr, segPtr = *prevPtrPtr;
  600.         segPtr != NULL;
  601.         prevPtrPtr = &(*prevPtrPtr)->nextPtr, segPtr = *prevPtrPtr) {
  602.         if (segPtr->typePtr->cleanupProc != NULL) {
  603.         *prevPtrPtr = (*segPtr->typePtr->cleanupProc)(segPtr, linePtr);
  604.         if (segPtr != *prevPtrPtr) {
  605.             anyChanges = 1;
  606.         }
  607.         }
  608.     }
  609.     if (!anyChanges) {
  610.         break;
  611.     }
  612.     }
  613. }
  614.  
  615. /*
  616.  *----------------------------------------------------------------------
  617.  *
  618.  * TkBTreeDeleteChars --
  619.  *
  620.  *    Delete a range of characters from a B-tree.  The caller
  621.  *    must make sure that the final newline of the B-tree is
  622.  *    never deleted.
  623.  *
  624.  * Results:
  625.  *    None.
  626.  *
  627.  * Side effects:
  628.  *    Information is deleted from the B-tree.  This can cause the
  629.  *    internal structure of the B-tree to change.  Note: because
  630.  *    of changes to the B-tree structure, the indices pointed
  631.  *    to by index1Ptr and index2Ptr should not be used after this
  632.  *    procedure returns.
  633.  *
  634.  *----------------------------------------------------------------------
  635.  */
  636.  
  637. void
  638. TkBTreeDeleteChars(index1Ptr, index2Ptr)
  639.     register TkTextIndex *index1Ptr;    /* Indicates first character that is
  640.                      * to be deleted. */
  641.     register TkTextIndex *index2Ptr;    /* Indicates character just after the
  642.                      * last one that is to be deleted. */
  643. {
  644.     TkTextSegment *prevPtr;        /* The segment just before the start
  645.                      * of the deletion range. */
  646.     TkTextSegment *lastPtr;        /* The segment just after the end
  647.                      * of the deletion range. */
  648.     TkTextSegment *segPtr, *nextPtr;
  649.     TkTextLine *curLinePtr;
  650.     Node *curNodePtr, *nodePtr;
  651.  
  652.     /*
  653.      * Tricky point:  split at index2Ptr first;  otherwise the split
  654.      * at index2Ptr may invalidate segPtr and/or prevPtr.
  655.      */
  656.  
  657.     lastPtr = SplitSeg(index2Ptr);
  658.     if (lastPtr != NULL) {
  659.     lastPtr = lastPtr->nextPtr;
  660.     }  else {
  661.     lastPtr = index2Ptr->linePtr->segPtr;
  662.     }
  663.     prevPtr = SplitSeg(index1Ptr);
  664.     if (prevPtr != NULL) {
  665.     segPtr = prevPtr->nextPtr;
  666.     prevPtr->nextPtr = lastPtr;
  667.     } else {
  668.     segPtr = index1Ptr->linePtr->segPtr;
  669.     index1Ptr->linePtr->segPtr = lastPtr;
  670.     }
  671.  
  672.     /*
  673.      * Delete all of the segments between prevPtr and lastPtr.
  674.      */
  675.  
  676.     curLinePtr = index1Ptr->linePtr;
  677.     curNodePtr = curLinePtr->parentPtr;
  678.     while (segPtr != lastPtr) {
  679.     if (segPtr == NULL) {
  680.         TkTextLine *nextLinePtr;
  681.  
  682.         /*
  683.          * We just ran off the end of a line.  First find the
  684.          * next line, then go back to the old line and delete it
  685.          * (unless it's the starting line for the range).
  686.          */
  687.  
  688.         nextLinePtr = TkBTreeNextLine(curLinePtr);
  689.         if (curLinePtr != index1Ptr->linePtr) {
  690.         if (curNodePtr == index1Ptr->linePtr->parentPtr) {
  691.             index1Ptr->linePtr->nextPtr = curLinePtr->nextPtr;
  692.         } else {
  693.             curNodePtr->children.linePtr = curLinePtr->nextPtr;
  694.         }
  695.         for (nodePtr = curNodePtr; nodePtr != NULL;
  696.             nodePtr = nodePtr->parentPtr) {
  697.             nodePtr->numLines--;
  698.         }
  699.         curNodePtr->numChildren--;
  700.         ckfree((char *) curLinePtr);
  701.         }
  702.         curLinePtr = nextLinePtr;
  703.         segPtr = curLinePtr->segPtr;
  704.  
  705.         /*
  706.          * If the node is empty then delete it and its parents,
  707.          * recursively upwards until a non-empty node is found.
  708.          */
  709.  
  710.         while (curNodePtr->numChildren == 0) {
  711.         Node *parentPtr;
  712.  
  713.         parentPtr = curNodePtr->parentPtr;
  714.         if (parentPtr->children.nodePtr == curNodePtr) {
  715.             parentPtr->children.nodePtr = curNodePtr->nextPtr;
  716.         } else {
  717.             Node *prevNodePtr = parentPtr->children.nodePtr;
  718.             while (prevNodePtr->nextPtr != curNodePtr) {
  719.             prevNodePtr = prevNodePtr->nextPtr;
  720.             }
  721.             prevNodePtr->nextPtr = curNodePtr->nextPtr;
  722.         }
  723.         parentPtr->numChildren--;
  724.         ckfree((char *) curNodePtr);
  725.         curNodePtr = parentPtr;
  726.         }
  727.         curNodePtr = curLinePtr->parentPtr;
  728.         continue;
  729.     }
  730.  
  731.     nextPtr = segPtr->nextPtr;
  732.     if ((*segPtr->typePtr->deleteProc)(segPtr, curLinePtr, 0) != 0) {
  733.         /*
  734.          * This segment refuses to die.  Move it to prevPtr and
  735.          * advance prevPtr if the segment has left gravity.
  736.          */
  737.  
  738.         if (prevPtr == NULL) {
  739.         segPtr->nextPtr = index1Ptr->linePtr->segPtr;
  740.         index1Ptr->linePtr->segPtr = segPtr;
  741.         } else {
  742.         segPtr->nextPtr = prevPtr->nextPtr;
  743.         prevPtr->nextPtr = segPtr;
  744.         }
  745.         if (segPtr->typePtr->leftGravity) {
  746.         prevPtr = segPtr;
  747.         }
  748.     }
  749.     segPtr = nextPtr;
  750.     }
  751.  
  752.     /*
  753.      * If the beginning and end of the deletion range are in different
  754.      * lines, join the two lines together and discard the ending line.
  755.      */
  756.  
  757.     if (index1Ptr->linePtr != index2Ptr->linePtr) {
  758.     TkTextLine *prevLinePtr;
  759.  
  760.     for (segPtr = lastPtr; segPtr != NULL;
  761.         segPtr = segPtr->nextPtr) {
  762.         if (segPtr->typePtr->lineChangeProc != NULL) {
  763.         (*segPtr->typePtr->lineChangeProc)(segPtr, index2Ptr->linePtr);
  764.         }
  765.     }
  766.     curNodePtr = index2Ptr->linePtr->parentPtr;
  767.     for (nodePtr = curNodePtr; nodePtr != NULL;
  768.         nodePtr = nodePtr->parentPtr) {
  769.         nodePtr->numLines--;
  770.     }
  771.     curNodePtr->numChildren--;
  772.     prevLinePtr = curNodePtr->children.linePtr;
  773.     if (prevLinePtr == index2Ptr->linePtr) {
  774.         curNodePtr->children.linePtr = index2Ptr->linePtr->nextPtr;
  775.     } else {
  776.         while (prevLinePtr->nextPtr != index2Ptr->linePtr) {
  777.         prevLinePtr = prevLinePtr->nextPtr;
  778.         }
  779.         prevLinePtr->nextPtr = index2Ptr->linePtr->nextPtr;
  780.     }
  781.     ckfree((char *) index2Ptr->linePtr);
  782.     Rebalance((BTree *) index2Ptr->tree, curNodePtr);
  783.     }
  784.  
  785.     /*
  786.      * Cleanup the segments in the new line.
  787.      */
  788.  
  789.     CleanupLine(index1Ptr->linePtr);
  790.  
  791.     /*
  792.      * Lastly, rebalance the first node of the range.
  793.      */
  794.  
  795.     Rebalance((BTree *) index1Ptr->tree, index1Ptr->linePtr->parentPtr);
  796.     if (tkBTreeDebug) {
  797.     TkBTreeCheck(index1Ptr->tree);
  798.     }
  799. }
  800.  
  801. /*
  802.  *----------------------------------------------------------------------
  803.  *
  804.  * TkBTreeFindLine --
  805.  *
  806.  *    Find a particular line in a B-tree based on its line number.
  807.  *
  808.  * Results:
  809.  *    The return value is a pointer to the line structure for the
  810.  *    line whose index is "line", or NULL if no such line exists.
  811.  *
  812.  * Side effects:
  813.  *    None.
  814.  *
  815.  *----------------------------------------------------------------------
  816.  */
  817.  
  818. TkTextLine *
  819. TkBTreeFindLine(tree, line)
  820.     TkTextBTree tree;            /* B-tree in which to find line. */
  821.     int line;                /* Index of desired line. */
  822. {
  823.     BTree *treePtr = (BTree *) tree;
  824.     register Node *nodePtr;
  825.     register TkTextLine *linePtr;
  826.     int linesLeft;
  827.  
  828.     nodePtr = treePtr->rootPtr;
  829.     linesLeft = line;
  830.     if ((line < 0) || (line >= nodePtr->numLines)) {
  831.     return NULL;
  832.     }
  833.  
  834.     /*
  835.      * Work down through levels of the tree until a node is found at
  836.      * level 0.
  837.      */
  838.  
  839.     while (nodePtr->level != 0) {
  840.     for (nodePtr = nodePtr->children.nodePtr;
  841.         nodePtr->numLines <= linesLeft;
  842.         nodePtr = nodePtr->nextPtr) {
  843.         if (nodePtr == NULL) {
  844.         panic("TkBTreeFindLine ran out of nodes");
  845.         }
  846.         linesLeft -= nodePtr->numLines;
  847.     }
  848.     }
  849.  
  850.     /*
  851.      * Work through the lines attached to the level-0 node.
  852.      */
  853.  
  854.     for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
  855.         linePtr = linePtr->nextPtr) {
  856.     if (linePtr == NULL) {
  857.         panic("TkBTreeFindLine ran out of lines");
  858.     }
  859.     linesLeft -= 1;
  860.     }
  861.     return linePtr;
  862. }
  863.  
  864. /*
  865.  *----------------------------------------------------------------------
  866.  *
  867.  * TkBTreeNextLine --
  868.  *
  869.  *    Given an existing line in a B-tree, this procedure locates the
  870.  *    next line in the B-tree.  This procedure is used for scanning
  871.  *    through the B-tree.
  872.  *
  873.  * Results:
  874.  *    The return value is a pointer to the line that immediately
  875.  *    follows linePtr, or NULL if there is no such line.
  876.  *
  877.  * Side effects:
  878.  *    None.
  879.  *
  880.  *----------------------------------------------------------------------
  881.  */
  882.  
  883. TkTextLine *
  884. TkBTreeNextLine(linePtr)
  885.     register TkTextLine *linePtr;    /* Pointer to existing line in
  886.                      * B-tree. */
  887. {
  888.     register Node *nodePtr;
  889.  
  890.     if (linePtr->nextPtr != NULL) {
  891.     return linePtr->nextPtr;
  892.     }
  893.  
  894.     /*
  895.      * This was the last line associated with the particular parent node.
  896.      * Search up the tree for the next node, then search down from that
  897.      * node to find the first line.
  898.      */
  899.  
  900.     for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
  901.     if (nodePtr->nextPtr != NULL) {
  902.         nodePtr = nodePtr->nextPtr;
  903.         break;
  904.     }
  905.     if (nodePtr->parentPtr == NULL) {
  906.         return (TkTextLine *) NULL;
  907.     }
  908.     }
  909.     while (nodePtr->level > 0) {
  910.     nodePtr = nodePtr->children.nodePtr;
  911.     }
  912.     return nodePtr->children.linePtr;
  913. }
  914.  
  915. /*
  916.  *----------------------------------------------------------------------
  917.  *
  918.  * TkBTreePreviousLine --
  919.  *
  920.  *    Given an existing line in a B-tree, this procedure locates the
  921.  *    previous line in the B-tree.  This procedure is used for scanning
  922.  *    through the B-tree in the reverse direction.
  923.  *
  924.  * Results:
  925.  *    The return value is a pointer to the line that immediately
  926.  *    preceeds linePtr, or NULL if there is no such line.
  927.  *
  928.  * Side effects:
  929.  *    None.
  930.  *
  931.  *----------------------------------------------------------------------
  932.  */
  933.  
  934. TkTextLine *
  935. TkBTreePreviousLine(linePtr)
  936.     register TkTextLine *linePtr;    /* Pointer to existing line in
  937.                      * B-tree. */
  938. {
  939.     register Node *nodePtr;
  940.     register Node *node2Ptr;
  941.     register TkTextLine *prevPtr;
  942.  
  943.     /*
  944.      * Find the line under this node just before the starting line.
  945.      */
  946.     prevPtr = linePtr->parentPtr->children.linePtr;    /* First line at leaf */
  947.     while (prevPtr != linePtr) {
  948.     if (prevPtr->nextPtr == linePtr) {
  949.         return prevPtr;
  950.     }
  951.     prevPtr = prevPtr->nextPtr;
  952.     if (prevPtr == (TkTextLine *) NULL) {
  953.         panic("TkBTreePreviousLine ran out of lines");
  954.     }
  955.     }
  956.  
  957.     /*
  958.      * This was the first line associated with the particular parent node.
  959.      * Search up the tree for the previous node, then search down from that
  960.      * node to find its last line.
  961.      */
  962.     for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
  963.     if (nodePtr == (Node *) NULL || nodePtr->parentPtr == (Node *) NULL) {
  964.         return (TkTextLine *) NULL;
  965.     }
  966.     if (nodePtr != nodePtr->parentPtr->children.nodePtr) {
  967.         break;
  968.     }
  969.     }
  970.     for (node2Ptr = nodePtr->parentPtr->children.nodePtr; ; 
  971.         node2Ptr = node2Ptr->children.nodePtr) {
  972.     while (node2Ptr->nextPtr != nodePtr) {
  973.         node2Ptr = node2Ptr->nextPtr;
  974.     }
  975.     if (node2Ptr->level == 0) {
  976.         break;
  977.     }
  978.     nodePtr = (Node *)NULL;
  979.     }
  980.     for (prevPtr = node2Ptr->children.linePtr ; ; prevPtr = prevPtr->nextPtr) {
  981.     if (prevPtr->nextPtr == (TkTextLine *) NULL) {
  982.         return prevPtr;
  983.     }
  984.     }
  985. }
  986.  
  987. /*
  988.  *----------------------------------------------------------------------
  989.  *
  990.  * TkBTreeLineIndex --
  991.  *
  992.  *    Given a pointer to a line in a B-tree, return the numerical
  993.  *    index of that line.
  994.  *
  995.  * Results:
  996.  *    The result is the index of linePtr within the tree, where 0
  997.  *    corresponds to the first line in the tree.
  998.  *
  999.  * Side effects:
  1000.  *    None.
  1001.  *
  1002.  *----------------------------------------------------------------------
  1003.  */
  1004.  
  1005. int
  1006. TkBTreeLineIndex(linePtr)
  1007.     TkTextLine *linePtr;        /* Pointer to existing line in
  1008.                      * B-tree. */
  1009. {
  1010.     register TkTextLine *linePtr2;
  1011.     register Node *nodePtr, *parentPtr, *nodePtr2;
  1012.     int index;
  1013.  
  1014.     /*
  1015.      * First count how many lines precede this one in its level-0
  1016.      * node.
  1017.      */
  1018.  
  1019.     nodePtr = linePtr->parentPtr;
  1020.     index = 0;
  1021.     for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
  1022.         linePtr2 = linePtr2->nextPtr) {
  1023.     if (linePtr2 == NULL) {
  1024.         panic("TkBTreeLineIndex couldn't find line");
  1025.     }
  1026.     index += 1;
  1027.     }
  1028.  
  1029.     /*
  1030.      * Now work up through the levels of the tree one at a time,
  1031.      * counting how many lines are in nodes preceding the current
  1032.      * node.
  1033.      */
  1034.  
  1035.     for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
  1036.         nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
  1037.     for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
  1038.         nodePtr2 = nodePtr2->nextPtr) {
  1039.         if (nodePtr2 == NULL) {
  1040.         panic("TkBTreeLineIndex couldn't find node");
  1041.         }
  1042.         index += nodePtr2->numLines;
  1043.     }
  1044.     }
  1045.     return index;
  1046. }
  1047.  
  1048. /*
  1049.  *----------------------------------------------------------------------
  1050.  *
  1051.  * TkBTreeLinkSegment --
  1052.  *
  1053.  *    This procedure adds a new segment to a B-tree at a given
  1054.  *    location.
  1055.  *
  1056.  * Results:
  1057.  *    None.
  1058.  *
  1059.  * Side effects:
  1060.  *    SegPtr will be linked into its tree.
  1061.  *
  1062.  *----------------------------------------------------------------------
  1063.  */
  1064.  
  1065.     /* ARGSUSED */
  1066. void
  1067. TkBTreeLinkSegment(segPtr, indexPtr)
  1068.     TkTextSegment *segPtr;    /* Pointer to new segment to be added to
  1069.                  * B-tree.  Should be completely initialized
  1070.                  * by caller except for nextPtr field. */
  1071.     TkTextIndex *indexPtr;    /* Where to add segment:  it gets linked
  1072.                  * in just before the segment indicated
  1073.                  * here. */
  1074. {
  1075.     register TkTextSegment *prevPtr;
  1076.  
  1077.     prevPtr = SplitSeg(indexPtr);
  1078.     if (prevPtr == NULL) {
  1079.     segPtr->nextPtr = indexPtr->linePtr->segPtr;
  1080.     indexPtr->linePtr->segPtr = segPtr;
  1081.     } else {
  1082.     segPtr->nextPtr = prevPtr->nextPtr;
  1083.     prevPtr->nextPtr = segPtr;
  1084.     }
  1085.     CleanupLine(indexPtr->linePtr);
  1086.     if (tkBTreeDebug) {
  1087.     TkBTreeCheck(indexPtr->tree);
  1088.     }
  1089. }
  1090.  
  1091. /*
  1092.  *----------------------------------------------------------------------
  1093.  *
  1094.  * TkBTreeUnlinkSegment --
  1095.  *
  1096.  *    This procedure unlinks a segment from its line in a B-tree.
  1097.  *
  1098.  * Results:
  1099.  *    None.
  1100.  *
  1101.  * Side effects:
  1102.  *    SegPtr will be unlinked from linePtr.  The segment itself
  1103.  *    isn't modified by this procedure.
  1104.  *
  1105.  *----------------------------------------------------------------------
  1106.  */
  1107.  
  1108.     /* ARGSUSED */
  1109. void
  1110. TkBTreeUnlinkSegment(tree, segPtr, linePtr)
  1111.     TkTextBTree tree;            /* Tree containing segment. */
  1112.     TkTextSegment *segPtr;        /* Segment to be unlinked. */
  1113.     TkTextLine *linePtr;        /* Line that currently contains
  1114.                      * segment. */
  1115. {
  1116.     register TkTextSegment *prevPtr;
  1117.  
  1118.     if (linePtr->segPtr == segPtr) {
  1119.     linePtr->segPtr = segPtr->nextPtr;
  1120.     } else {
  1121.     for (prevPtr = linePtr->segPtr; prevPtr->nextPtr != segPtr;
  1122.         prevPtr = prevPtr->nextPtr) {
  1123.         /* Empty loop body. */
  1124.     }
  1125.     prevPtr->nextPtr = segPtr->nextPtr;
  1126.     }
  1127.     CleanupLine(linePtr);
  1128. }
  1129.  
  1130. /*
  1131.  *----------------------------------------------------------------------
  1132.  *
  1133.  * TkBTreeTag --
  1134.  *
  1135.  *    Turn a given tag on or off for a given range of characters in
  1136.  *    a B-tree of text.
  1137.  *
  1138.  * Results:
  1139.  *    None.
  1140.  *
  1141.  * Side effects:
  1142.  *    The given tag is added to the given range of characters
  1143.  *    in the tree or removed from all those characters, depending
  1144.  *    on the "add" argument.  The structure of the btree is modified
  1145.  *    enough that index1Ptr and index2Ptr are no longer valid after
  1146.  *    this procedure returns, and the indexes may be modified by
  1147.  *    this procedure.
  1148.  *
  1149.  *----------------------------------------------------------------------
  1150.  */
  1151.  
  1152. void
  1153. TkBTreeTag(index1Ptr, index2Ptr, tagPtr, add)
  1154.     register TkTextIndex *index1Ptr;    /* Indicates first character in
  1155.                      * range. */
  1156.     register TkTextIndex *index2Ptr;    /* Indicates character just after the
  1157.                      * last one in range. */
  1158.     TkTextTag *tagPtr;            /* Tag to add or remove. */
  1159.     int add;                /* One means add tag to the given
  1160.                      * range of characters;  zero means
  1161.                      * remove the tag from the range. */
  1162. {
  1163.     TkTextSegment *segPtr, *prevPtr;
  1164.     TkTextSearch search;
  1165.     TkTextLine *cleanupLinePtr;
  1166.     int oldState;
  1167.     int changed;
  1168.  
  1169.     /*
  1170.      * See whether the tag is present at the start of the range.  If
  1171.      * the state doesn't already match what we want then add a toggle
  1172.      * there.
  1173.      */
  1174.  
  1175.     oldState = TkBTreeCharTagged(index1Ptr, tagPtr);
  1176.     if ((add != 0) ^ oldState) {
  1177.     segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
  1178.     segPtr->typePtr = (add) ? &tkTextToggleOnType : &tkTextToggleOffType;
  1179.     prevPtr = SplitSeg(index1Ptr);
  1180.     if (prevPtr == NULL) {
  1181.         segPtr->nextPtr = index1Ptr->linePtr->segPtr;
  1182.         index1Ptr->linePtr->segPtr = segPtr;
  1183.     } else {
  1184.         segPtr->nextPtr = prevPtr->nextPtr;
  1185.         prevPtr->nextPtr = segPtr;
  1186.     }
  1187.     segPtr->size = 0;
  1188.     segPtr->body.toggle.tagPtr = tagPtr;
  1189.     segPtr->body.toggle.inNodeCounts = 0;
  1190.     }
  1191.  
  1192.     /*
  1193.      * Scan the range of characters and delete any internal tag
  1194.      * transitions.  Keep track of what the old state was at the end
  1195.      * of the range, and add a toggle there if it's needed.
  1196.      */
  1197.  
  1198.     TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  1199.     cleanupLinePtr = index1Ptr->linePtr;
  1200.     while (TkBTreeNextTag(&search)) {
  1201.     oldState ^= 1;
  1202.     segPtr = search.segPtr;
  1203.     prevPtr = search.curIndex.linePtr->segPtr;
  1204.     if (prevPtr == segPtr) {
  1205.         search.curIndex.linePtr->segPtr = segPtr->nextPtr;
  1206.     } else {
  1207.         while (prevPtr->nextPtr != segPtr) {
  1208.         prevPtr = prevPtr->nextPtr;
  1209.         }
  1210.         prevPtr->nextPtr = segPtr->nextPtr;
  1211.     }
  1212.     if (segPtr->body.toggle.inNodeCounts) {
  1213.         ChangeNodeToggleCount(search.curIndex.linePtr->parentPtr,
  1214.             segPtr->body.toggle.tagPtr, -1);
  1215.         segPtr->body.toggle.inNodeCounts = 0;
  1216.         changed = 1;
  1217.     } else {
  1218.         changed = 0;
  1219.     }
  1220.     ckfree((char *) segPtr);
  1221.  
  1222.     /*
  1223.      * The code below is a bit tricky.  After deleting a toggle
  1224.      * we eventually have to call CleanupLine, in order to allow
  1225.      * character segments to be merged together.  To do this, we
  1226.      * remember in cleanupLinePtr a line that needs to be
  1227.      * cleaned up, but we don't clean it up until we've moved
  1228.      * on to a different line.  That way the cleanup process
  1229.      * won't goof up segPtr.
  1230.      */
  1231.  
  1232.     if (cleanupLinePtr != search.curIndex.linePtr) {
  1233.         CleanupLine(cleanupLinePtr);
  1234.         cleanupLinePtr = search.curIndex.linePtr;
  1235.     }
  1236.     /*
  1237.      * Quick hack.  ChangeNodeToggleCount may move the tag's root
  1238.      * location around and leave the search in the void.  This resets
  1239.      * the search.
  1240.      */
  1241.     if (changed) {
  1242.         TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  1243.     }
  1244.     }
  1245.     if ((add != 0) ^ oldState) {
  1246.     segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
  1247.     segPtr->typePtr = (add) ? &tkTextToggleOffType : &tkTextToggleOnType;
  1248.     prevPtr = SplitSeg(index2Ptr);
  1249.     if (prevPtr == NULL) {
  1250.         segPtr->nextPtr = index2Ptr->linePtr->segPtr;
  1251.         index2Ptr->linePtr->segPtr = segPtr;
  1252.     } else {
  1253.         segPtr->nextPtr = prevPtr->nextPtr;
  1254.         prevPtr->nextPtr = segPtr;
  1255.     }
  1256.     segPtr->size = 0;
  1257.     segPtr->body.toggle.tagPtr = tagPtr;
  1258.     segPtr->body.toggle.inNodeCounts = 0;
  1259.     }
  1260.  
  1261.     /*
  1262.      * Cleanup cleanupLinePtr and the last line of the range, if
  1263.      * these are different.
  1264.      */
  1265.  
  1266.     CleanupLine(cleanupLinePtr);
  1267.     if (cleanupLinePtr != index2Ptr->linePtr) {
  1268.     CleanupLine(index2Ptr->linePtr);
  1269.     }
  1270.  
  1271.     if (tkBTreeDebug) {
  1272.     TkBTreeCheck(index1Ptr->tree);
  1273.     }
  1274. }
  1275.  
  1276. /*
  1277.  *----------------------------------------------------------------------
  1278.  *
  1279.  * ChangeNodeToggleCount --
  1280.  *
  1281.  *    This procedure increments or decrements the toggle count for
  1282.  *    a particular tag in a particular node and all its ancestors
  1283.  *    up to the per-tag root node.
  1284.  *
  1285.  * Results:
  1286.  *    None.
  1287.  *
  1288.  * Side effects:
  1289.  *    The toggle count for tag is adjusted up or down by "delta" in
  1290.  *    nodePtr.  This routine maintains the tagRootPtr that identifies
  1291.  *    the root node for the tag, moving it up or down the tree as needed.
  1292.  *
  1293.  *----------------------------------------------------------------------
  1294.  */
  1295.  
  1296. static void
  1297. ChangeNodeToggleCount(nodePtr, tagPtr, delta)
  1298.     register Node *nodePtr;        /* Node whose toggle count for a tag
  1299.                      * must be changed. */
  1300.     TkTextTag *tagPtr;            /* Information about tag. */
  1301.     int delta;                /* Amount to add to current toggle
  1302.                      * count for tag (may be negative). */
  1303. {
  1304.     register Summary *summaryPtr, *prevPtr;
  1305.     register Node *node2Ptr;
  1306.     int rootLevel;            /* Level of original tag root */
  1307.  
  1308.     tagPtr->toggleCount += delta;
  1309.     if (tagPtr->tagRootPtr == (Node *) NULL) {
  1310.     tagPtr->tagRootPtr = nodePtr;
  1311.     return;
  1312.     }
  1313.  
  1314.     /*
  1315.      * Note the level of the existing root for the tag so we can detect
  1316.      * if it needs to be moved because of the toggle count change.
  1317.      */
  1318.  
  1319.     rootLevel = tagPtr->tagRootPtr->level;
  1320.  
  1321.     /*
  1322.      * Iterate over the node and its ancestors up to the tag root, adjusting
  1323.      * summary counts at each node and moving the tag's root upwards if
  1324.      * necessary.
  1325.      */
  1326.  
  1327.     for ( ; nodePtr != tagPtr->tagRootPtr; nodePtr = nodePtr->parentPtr) {
  1328.     /*
  1329.      * See if there's already an entry for this tag for this node.  If so,
  1330.      * perhaps all we have to do is adjust its count.
  1331.      */
  1332.     
  1333.     for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
  1334.         summaryPtr != NULL;
  1335.         prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1336.         if (summaryPtr->tagPtr == tagPtr) {
  1337.         break;
  1338.         }
  1339.     }
  1340.     if (summaryPtr != NULL) {
  1341.         summaryPtr->toggleCount += delta;
  1342.         if (summaryPtr->toggleCount > 0 &&
  1343.             summaryPtr->toggleCount < tagPtr->toggleCount) {
  1344.         continue;
  1345.         }
  1346.         if (summaryPtr->toggleCount != 0) {
  1347.         /*
  1348.          * Should never find a node with max toggle count at this
  1349.          * point (there shouldn't have been a summary entry in the
  1350.          * first place).
  1351.          */
  1352.  
  1353.         panic("ChangeNodeToggleCount: bad toggle count (%d) max (%d)",
  1354.             summaryPtr->toggleCount, tagPtr->toggleCount);
  1355.         }
  1356.     
  1357.         /*
  1358.          * Zero toggle count;  must remove this tag from the list.
  1359.          */
  1360.  
  1361.         if (prevPtr == NULL) {
  1362.         nodePtr->summaryPtr = summaryPtr->nextPtr;
  1363.         } else {
  1364.         prevPtr->nextPtr = summaryPtr->nextPtr;
  1365.         }
  1366.         ckfree((char *) summaryPtr);
  1367.     } else {
  1368.         /*
  1369.          * This tag isn't currently in the summary information list.
  1370.          */
  1371.     
  1372.         if (rootLevel == nodePtr->level) {
  1373.     
  1374.         /*
  1375.          * The old tag root is at the same level in the tree as this
  1376.          * node, but it isn't at this node.  Move the tag root up
  1377.          * a level, in the hopes that it will now cover this node
  1378.          * as well as the old root (if not, we'll move it up again
  1379.          * the next time through the loop).  To push it up one level
  1380.          * we copy the original toggle count into the summary
  1381.          * information at the old root and change the root to its
  1382.          * parent node.
  1383.          */
  1384.     
  1385.         Node *rootNodePtr = tagPtr->tagRootPtr;
  1386.         summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1387.         summaryPtr->tagPtr = tagPtr;
  1388.         summaryPtr->toggleCount = tagPtr->toggleCount - delta;
  1389.         summaryPtr->nextPtr = rootNodePtr->summaryPtr;
  1390.         rootNodePtr->summaryPtr = summaryPtr;
  1391.         rootNodePtr = rootNodePtr->parentPtr;
  1392.         rootLevel = rootNodePtr->level;
  1393.         tagPtr->tagRootPtr = rootNodePtr;
  1394.         }
  1395.         summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1396.         summaryPtr->tagPtr = tagPtr;
  1397.         summaryPtr->toggleCount = delta;
  1398.         summaryPtr->nextPtr = nodePtr->summaryPtr;
  1399.         nodePtr->summaryPtr = summaryPtr;
  1400.     }
  1401.     }
  1402.  
  1403.     /*
  1404.      * If we've decremented the toggle count, then it may be necessary
  1405.      * to push the tag root down one or more levels.
  1406.      */
  1407.  
  1408.     if (delta >= 0) {
  1409.     return;
  1410.     }
  1411.     if (tagPtr->toggleCount == 0) {
  1412.     tagPtr->tagRootPtr = (Node *) NULL;
  1413.     return;
  1414.     }
  1415.     nodePtr = tagPtr->tagRootPtr;
  1416.     while (nodePtr->level > 0) {
  1417.     /*
  1418.      * See if a single child node accounts for all of the tag's
  1419.      * toggles.  If so, push the root down one level.
  1420.      */
  1421.  
  1422.     for (node2Ptr = nodePtr->children.nodePtr;
  1423.         node2Ptr != (Node *)NULL ;
  1424.         node2Ptr = node2Ptr->nextPtr) {
  1425.         for (prevPtr = NULL, summaryPtr = node2Ptr->summaryPtr;
  1426.             summaryPtr != NULL;
  1427.             prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1428.         if (summaryPtr->tagPtr == tagPtr) {
  1429.             break;
  1430.         }
  1431.         }
  1432.         if (summaryPtr == NULL) {
  1433.         continue;
  1434.         }
  1435.         if (summaryPtr->toggleCount != tagPtr->toggleCount) {
  1436.         /*
  1437.          * No node has all toggles, so the root is still valid.
  1438.          */
  1439.  
  1440.         return;
  1441.         }
  1442.  
  1443.         /*
  1444.          * This node has all the toggles, so push down the root.
  1445.          */
  1446.  
  1447.         if (prevPtr == NULL) {
  1448.         node2Ptr->summaryPtr = summaryPtr->nextPtr;
  1449.         } else {
  1450.         prevPtr->nextPtr = summaryPtr->nextPtr;
  1451.         }
  1452.         ckfree((char *) summaryPtr);
  1453.         tagPtr->tagRootPtr = node2Ptr;
  1454.         break;
  1455.     }
  1456.     nodePtr = tagPtr->tagRootPtr;
  1457.     }
  1458. }
  1459.  
  1460. /*
  1461.  *----------------------------------------------------------------------
  1462.  *
  1463.  * FindTagStart --
  1464.  *
  1465.  *    Find the start of the first range of a tag.
  1466.  *
  1467.  * Results:
  1468.  *    The return value is a pointer to the first tag toggle segment
  1469.  *    for the tag.  This can be either a tagon or tagoff segments because
  1470.  *    of the way TkBTreeAdd removes a tag.
  1471.  *    Sets *indexPtr to be the index of the tag toggle.
  1472.  *
  1473.  * Side effects:
  1474.  *    None.
  1475.  *
  1476.  *----------------------------------------------------------------------
  1477.  */
  1478.  
  1479. static TkTextSegment *
  1480. FindTagStart(tree, tagPtr, indexPtr)
  1481.     TkTextBTree tree;            /* Tree to search within */
  1482.     TkTextTag *tagPtr;            /* Tag to search for. */
  1483.     TkTextIndex *indexPtr;        /* Return - index information */
  1484. {
  1485.     register Node *nodePtr;
  1486.     register TkTextLine *linePtr;
  1487.     register TkTextSegment *segPtr;
  1488.     register Summary *summaryPtr;
  1489.     int offset = 0;
  1490.  
  1491.     nodePtr = tagPtr->tagRootPtr;
  1492.     if (nodePtr == (Node *) NULL) {
  1493.     return NULL;
  1494.     }
  1495.  
  1496.     /*
  1497.      * Search from the root of the subtree that contains the tag down
  1498.      * to the level 0 node.
  1499.      */
  1500.  
  1501.     while (nodePtr->level > 0) {
  1502.     for (nodePtr = nodePtr->children.nodePtr ; nodePtr != (Node *) NULL;
  1503.         nodePtr = nodePtr->nextPtr) {
  1504.         for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1505.             summaryPtr = summaryPtr->nextPtr) {
  1506.         if (summaryPtr->tagPtr == tagPtr) {
  1507.             goto gotNodeWithTag;
  1508.         }
  1509.         }
  1510.     }
  1511.     gotNodeWithTag:
  1512.     continue;
  1513.     }
  1514.  
  1515.     /*
  1516.      * Work through the lines attached to the level-0 node.
  1517.      */
  1518.  
  1519.     for (linePtr = nodePtr->children.linePtr; linePtr != (TkTextLine *) NULL;
  1520.         linePtr = linePtr->nextPtr) {
  1521.     for (offset = 0, segPtr = linePtr->segPtr ; segPtr != NULL;
  1522.         offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1523.         if (((segPtr->typePtr == &tkTextToggleOnType)
  1524.             || (segPtr->typePtr == &tkTextToggleOffType))
  1525.             && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1526.         /*
  1527.          * It is possible that this is a tagoff tag, but that
  1528.          * gets cleaned up later.
  1529.          */
  1530.         indexPtr->tree = tree;
  1531.         indexPtr->linePtr = linePtr;
  1532.         indexPtr->charIndex = offset;
  1533.         return segPtr;
  1534.         }
  1535.     }
  1536.     }
  1537.     return NULL;
  1538. }
  1539.  
  1540. /*
  1541.  *----------------------------------------------------------------------
  1542.  *
  1543.  * FindTagEnd --
  1544.  *
  1545.  *    Find the end of the last range of a tag.
  1546.  *
  1547.  * Results:
  1548.  *    The return value is a pointer to the last tag toggle segment
  1549.  *    for the tag.  This can be either a tagon or tagoff segments because
  1550.  *    of the way TkBTreeAdd removes a tag.
  1551.  *    Sets *indexPtr to be the index of the tag toggle.
  1552.  *
  1553.  * Side effects:
  1554.  *    None.
  1555.  *
  1556.  *----------------------------------------------------------------------
  1557.  */
  1558.  
  1559. static TkTextSegment *
  1560. FindTagEnd(tree, tagPtr, indexPtr)
  1561.     TkTextBTree tree;            /* Tree to search within */
  1562.     TkTextTag *tagPtr;            /* Tag to search for. */
  1563.     TkTextIndex *indexPtr;        /* Return - index information */
  1564. {
  1565.     register Node *nodePtr, *lastNodePtr;
  1566.     register TkTextLine *linePtr ,*lastLinePtr;
  1567.     register TkTextSegment *segPtr, *lastSegPtr, *last2SegPtr;
  1568.     register Summary *summaryPtr;
  1569.     int lastoffset, lastoffset2, offset = 0;
  1570.  
  1571.     nodePtr = tagPtr->tagRootPtr;
  1572.     if (nodePtr == (Node *) NULL) {
  1573.     return NULL;
  1574.     }
  1575.  
  1576.     /*
  1577.      * Search from the root of the subtree that contains the tag down
  1578.      * to the level 0 node.
  1579.      */
  1580.  
  1581.     while (nodePtr->level > 0) {
  1582.     for (lastNodePtr = NULL, nodePtr = nodePtr->children.nodePtr ;
  1583.         nodePtr != (Node *) NULL; nodePtr = nodePtr->nextPtr) {
  1584.         for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1585.             summaryPtr = summaryPtr->nextPtr) {
  1586.         if (summaryPtr->tagPtr == tagPtr) {
  1587.             lastNodePtr = nodePtr;
  1588.             break;
  1589.         }
  1590.         }
  1591.     }
  1592.     nodePtr = lastNodePtr;
  1593.     }
  1594.  
  1595.     /*
  1596.      * Work through the lines attached to the level-0 node.
  1597.      */
  1598.     last2SegPtr = NULL;
  1599.     lastoffset2 = 0;
  1600.     lastoffset = 0;
  1601.     for (lastLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  1602.         linePtr != (TkTextLine *) NULL; linePtr = linePtr->nextPtr) {
  1603.     for (offset = 0, lastSegPtr = NULL, segPtr = linePtr->segPtr ;
  1604.         segPtr != NULL; 
  1605.         offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1606.         if (((segPtr->typePtr == &tkTextToggleOnType)
  1607.             || (segPtr->typePtr == &tkTextToggleOffType))
  1608.             && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1609.         lastSegPtr = segPtr;
  1610.         lastoffset = offset;
  1611.         }
  1612.     }
  1613.     if (lastSegPtr != NULL) {
  1614.         lastLinePtr = linePtr;
  1615.         last2SegPtr = lastSegPtr;
  1616.         lastoffset2 = lastoffset;
  1617.     }
  1618.     }
  1619.     indexPtr->tree = tree;
  1620.     indexPtr->linePtr = lastLinePtr;
  1621.     indexPtr->charIndex = lastoffset2;
  1622.     return last2SegPtr;
  1623. }
  1624.  
  1625. /*
  1626.  *----------------------------------------------------------------------
  1627.  *
  1628.  * TkBTreeStartSearch --
  1629.  *
  1630.  *    This procedure sets up a search for tag transitions involving
  1631.  *    a given tag (or all tags) in a given range of the text.
  1632.  *
  1633.  * Results:
  1634.  *    None.
  1635.  *
  1636.  * Side effects:
  1637.  *    The information at *searchPtr is set up so that subsequent calls
  1638.  *    to TkBTreeNextTag or TkBTreePrevTag will return information about the
  1639.  *    locations of tag transitions.  Note that TkBTreeNextTag or 
  1640.  *    TkBTreePrevTag must be called to get the first transition.
  1641.  *    Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1642.  *    guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
  1643.  *    greater than that if *index1Ptr is less than the first tag transition.
  1644.  *
  1645.  *----------------------------------------------------------------------
  1646.  */
  1647.  
  1648. void
  1649. TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1650.     TkTextIndex *index1Ptr;        /* Search starts here.  Tag toggles
  1651.                      * at this position will not be
  1652.                      * returned. */
  1653.     TkTextIndex *index2Ptr;        /* Search stops here.  Tag toggles
  1654.                      * at this position *will* be
  1655.                      * returned. */
  1656.     TkTextTag *tagPtr;            /* Tag to search for.  NULL means
  1657.                      * search for any tag. */
  1658.     register TkTextSearch *searchPtr;    /* Where to store information about
  1659.                      * search's progress. */
  1660. {
  1661.     int offset;
  1662.     TkTextIndex index0;        /* First index of the tag */
  1663.     TkTextSegment *seg0Ptr;    /* First segment of the tag */
  1664.  
  1665.     /*
  1666.      * Find the segment that contains the first toggle for the tag.  This
  1667.      * may become the starting point in the search.
  1668.      */
  1669.  
  1670.     seg0Ptr = FindTagStart(index1Ptr->tree, tagPtr, &index0);
  1671.     if (seg0Ptr == (TkTextSegment *) NULL) {
  1672.     /*
  1673.      * Even though there are no toggles, the display code still
  1674.      * uses the search curIndex, so initialize that anyway.
  1675.      */
  1676.  
  1677.     searchPtr->linesLeft = 0;
  1678.     searchPtr->curIndex = *index1Ptr;
  1679.     searchPtr->segPtr = NULL;
  1680.     searchPtr->nextPtr = NULL;
  1681.     return;
  1682.     }
  1683.     if (TkTextIndexCmp(index1Ptr, &index0) < 0) {
  1684.     /*
  1685.      * Adjust start of search up to the first range of the tag
  1686.      */
  1687.  
  1688.     searchPtr->curIndex = index0;
  1689.     searchPtr->segPtr = NULL;
  1690.     searchPtr->nextPtr = seg0Ptr;    /* Will be returned by NextTag */
  1691.     index1Ptr = &index0;
  1692.     } else {
  1693.     searchPtr->curIndex = *index1Ptr;
  1694.     searchPtr->segPtr = NULL;
  1695.     searchPtr->nextPtr = TkTextIndexToSeg(index1Ptr, &offset);
  1696.     searchPtr->curIndex.charIndex -= offset;
  1697.     }
  1698.     searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, (int *) NULL);
  1699.     searchPtr->tagPtr = tagPtr;
  1700.     searchPtr->linesLeft = TkBTreeLineIndex(index2Ptr->linePtr) + 1
  1701.         - TkBTreeLineIndex(index1Ptr->linePtr);
  1702.     searchPtr->allTags = (tagPtr == NULL);
  1703.     if (searchPtr->linesLeft == 1) {
  1704.     /*
  1705.      * Starting and stopping segments are in the same line; mark the
  1706.      * search as over immediately if the second segment is before the
  1707.      * first.  A search does not return a toggle at the very start of
  1708.      * the range, unless the range is artificially moved up to index0.
  1709.      */
  1710.     if (((index1Ptr == &index0) && 
  1711.         (index1Ptr->charIndex > index2Ptr->charIndex)) ||
  1712.         ((index1Ptr != &index0) && 
  1713.         (index1Ptr->charIndex >= index2Ptr->charIndex))) {
  1714.         searchPtr->linesLeft = 0;
  1715.     }
  1716.     }
  1717. }
  1718.  
  1719. /*
  1720.  *----------------------------------------------------------------------
  1721.  *
  1722.  * TkBTreeStartSearchBack --
  1723.  *
  1724.  *    This procedure sets up a search backwards for tag transitions involving
  1725.  *    a given tag (or all tags) in a given range of the text.  In the
  1726.  *    normal case the first index (*index1Ptr) is beyond the second
  1727.  *    index (*index2Ptr).
  1728.  *    
  1729.  *
  1730.  * Results:
  1731.  *    None.
  1732.  *
  1733.  * Side effects:
  1734.  *    The information at *searchPtr is set up so that subsequent calls
  1735.  *    to TkBTreePrevTag will return information about the
  1736.  *    locations of tag transitions.  Note that TkBTreePrevTag must be called
  1737.  *    to get the first transition.
  1738.  *    Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1739.  *    guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
  1740.  *    less than that if *index1Ptr is greater than the last tag transition.
  1741.  *
  1742.  *----------------------------------------------------------------------
  1743.  */
  1744.  
  1745. void
  1746. TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1747.     TkTextIndex *index1Ptr;        /* Search starts here.  Tag toggles
  1748.                      * at this position will not be
  1749.                      * returned. */
  1750.     TkTextIndex *index2Ptr;        /* Search stops here.  Tag toggles
  1751.                      * at this position *will* be
  1752.                      * returned. */
  1753.     TkTextTag *tagPtr;            /* Tag to search for.  NULL means
  1754.                      * search for any tag. */
  1755.     register TkTextSearch *searchPtr;    /* Where to store information about
  1756.                      * search's progress. */
  1757. {
  1758.     int offset;
  1759.     TkTextIndex index0;        /* Last index of the tag */
  1760.     TkTextIndex backOne;    /* One character before starting index */
  1761.     TkTextSegment *seg0Ptr;    /* Last segment of the tag */
  1762.  
  1763.     /*
  1764.      * Find the segment that contains the last toggle for the tag.  This
  1765.      * may become the starting point in the search.
  1766.      */
  1767.  
  1768.     seg0Ptr = FindTagEnd(index1Ptr->tree, tagPtr, &index0);
  1769.     if (seg0Ptr == (TkTextSegment *) NULL) {
  1770.     /*
  1771.      * Even though there are no toggles, the display code still
  1772.      * uses the search curIndex, so initialize that anyway.
  1773.      */
  1774.  
  1775.     searchPtr->linesLeft = 0;
  1776.     searchPtr->curIndex = *index1Ptr;
  1777.     searchPtr->segPtr = NULL;
  1778.     searchPtr->nextPtr = NULL;
  1779.     return;
  1780.     }
  1781.  
  1782.     /*
  1783.      * Adjust the start of the search so it doesn't find any tag toggles
  1784.      * that are right at the index specified by the user.
  1785.      */
  1786.  
  1787.     if (TkTextIndexCmp(index1Ptr, &index0) > 0) {
  1788.     searchPtr->curIndex = index0;
  1789.     index1Ptr = &index0;
  1790.     } else {
  1791.     TkTextIndexBackChars(index1Ptr, 1, &searchPtr->curIndex);
  1792.     }
  1793.     searchPtr->segPtr = NULL;
  1794.     searchPtr->nextPtr = TkTextIndexToSeg(&searchPtr->curIndex, &offset);
  1795.     searchPtr->curIndex.charIndex -= offset;
  1796.  
  1797.     /*
  1798.      * Adjust the end of the search so it does find toggles that are right
  1799.      * at the second index specified by the user.
  1800.      */
  1801.  
  1802.     if ((TkBTreeLineIndex(index2Ptr->linePtr) == 0) &&
  1803.         (index2Ptr->charIndex == 0)) {
  1804.     backOne = *index2Ptr;
  1805.     searchPtr->lastPtr = NULL;    /* Signals special case for 1.0 */
  1806.     } else {
  1807.     TkTextIndexBackChars(index2Ptr, 1, &backOne);
  1808.     searchPtr->lastPtr = TkTextIndexToSeg(&backOne, (int *) NULL);
  1809.     }
  1810.     searchPtr->tagPtr = tagPtr;
  1811.     searchPtr->linesLeft = TkBTreeLineIndex(index1Ptr->linePtr) + 1
  1812.         - TkBTreeLineIndex(backOne.linePtr);
  1813.     searchPtr->allTags = (tagPtr == NULL);
  1814.     if (searchPtr->linesLeft == 1) {
  1815.     /*
  1816.      * Starting and stopping segments are in the same line; mark the
  1817.      * search as over immediately if the second segment is after the
  1818.      * first.
  1819.      */
  1820.  
  1821.     if (index1Ptr->charIndex <= backOne.charIndex) {
  1822.         searchPtr->linesLeft = 0;
  1823.     }
  1824.     }
  1825. }
  1826.  
  1827. /*
  1828.  *----------------------------------------------------------------------
  1829.  *
  1830.  * TkBTreeNextTag --
  1831.  *
  1832.  *    Once a tag search has begun, successive calls to this procedure
  1833.  *    return successive tag toggles.  Note:  it is NOT SAFE to call this
  1834.  *    procedure if characters have been inserted into or deleted from
  1835.  *    the B-tree since the call to TkBTreeStartSearch.
  1836.  *
  1837.  * Results:
  1838.  *    The return value is 1 if another toggle was found that met the
  1839.  *    criteria specified in the call to TkBTreeStartSearch;  in this
  1840.  *    case searchPtr->curIndex gives the toggle's position and
  1841.  *    searchPtr->curTagPtr points to its segment.  0 is returned if
  1842.  *    no more matching tag transitions were found; in this case
  1843.  *    searchPtr->curIndex is the same as searchPtr->stopIndex.
  1844.  *
  1845.  * Side effects:
  1846.  *    Information in *searchPtr is modified to update the state of the
  1847.  *    search and indicate where the next tag toggle is located.
  1848.  *
  1849.  *----------------------------------------------------------------------
  1850.  */
  1851.  
  1852. int
  1853. TkBTreeNextTag(searchPtr)
  1854.     register TkTextSearch *searchPtr;    /* Information about search in
  1855.                      * progress;  must have been set up by
  1856.                      * call to TkBTreeStartSearch. */
  1857. {
  1858.     register TkTextSegment *segPtr;
  1859.     register Node *nodePtr;
  1860.     register Summary *summaryPtr;
  1861.  
  1862.     if (searchPtr->linesLeft <= 0) {
  1863.     goto searchOver;
  1864.     }
  1865.  
  1866.     /*
  1867.      * The outermost loop iterates over lines that may potentially contain
  1868.      * a relevant tag transition, starting from the current segment in
  1869.      * the current line.
  1870.      */
  1871.  
  1872.     segPtr = searchPtr->nextPtr;
  1873.     while (1) {
  1874.     /*
  1875.      * Check for more tags on the current line.
  1876.      */
  1877.  
  1878.     for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
  1879.         if (segPtr == searchPtr->lastPtr) {
  1880.         goto searchOver;
  1881.         }
  1882.         if (((segPtr->typePtr == &tkTextToggleOnType)
  1883.             || (segPtr->typePtr == &tkTextToggleOffType))
  1884.             && (searchPtr->allTags
  1885.             || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
  1886.         searchPtr->segPtr = segPtr;
  1887.         searchPtr->nextPtr = segPtr->nextPtr;
  1888.         searchPtr->tagPtr = segPtr->body.toggle.tagPtr;
  1889.         return 1;
  1890.         }
  1891.         searchPtr->curIndex.charIndex += segPtr->size;
  1892.     }
  1893.     
  1894.     /*
  1895.      * See if there are more lines associated with the current parent
  1896.      * node.  If so, go back to the top of the loop to search the next
  1897.      * one.
  1898.      */
  1899.  
  1900.     nodePtr = searchPtr->curIndex.linePtr->parentPtr;
  1901.     searchPtr->curIndex.linePtr = searchPtr->curIndex.linePtr->nextPtr;
  1902.     searchPtr->linesLeft--;
  1903.     if (searchPtr->linesLeft <= 0) {
  1904.         goto searchOver;
  1905.     }
  1906.     if (searchPtr->curIndex.linePtr != NULL) {
  1907.         segPtr = searchPtr->curIndex.linePtr->segPtr;
  1908.         searchPtr->curIndex.charIndex = 0;
  1909.         continue;
  1910.     }
  1911.     if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
  1912.         goto searchOver;
  1913.     }
  1914.     
  1915.     /*
  1916.      * Search across and up through the B-tree's node hierarchy looking
  1917.      * for the next node that has a relevant tag transition somewhere in
  1918.      * its subtree.  Be sure to update linesLeft as we skip over large
  1919.      * chunks of lines.
  1920.      */
  1921.     
  1922.     while (1) {
  1923.         while (nodePtr->nextPtr == NULL) {
  1924.         if (nodePtr->parentPtr == NULL ||
  1925.             nodePtr->parentPtr == searchPtr->tagPtr->tagRootPtr) {
  1926.             goto searchOver;
  1927.         }
  1928.         nodePtr = nodePtr->parentPtr;
  1929.         }
  1930.         nodePtr = nodePtr->nextPtr;
  1931.         for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  1932.             summaryPtr = summaryPtr->nextPtr) {
  1933.         if ((searchPtr->allTags) ||
  1934.             (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1935.             goto gotNodeWithTag;
  1936.         }
  1937.         }
  1938.         searchPtr->linesLeft -= nodePtr->numLines;
  1939.     }
  1940.     
  1941.     /*
  1942.      * At this point we've found a subtree that has a relevant tag
  1943.      * transition.  Now search down (and across) through that subtree
  1944.      * to find the first level-0 node that has a relevant tag transition.
  1945.      */
  1946.     
  1947.     gotNodeWithTag:
  1948.     while (nodePtr->level > 0) {
  1949.         for (nodePtr = nodePtr->children.nodePtr; ;
  1950.             nodePtr = nodePtr->nextPtr) {
  1951.         for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  1952.             summaryPtr = summaryPtr->nextPtr) {
  1953.             if ((searchPtr->allTags)
  1954.                 || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1955.             goto nextChild;
  1956.             }
  1957.         }
  1958.         searchPtr->linesLeft -= nodePtr->numLines;
  1959.         if (nodePtr->nextPtr == NULL) {
  1960.             panic("TkBTreeNextTag found incorrect tag summary info.");
  1961.         }
  1962.         }
  1963.         nextChild:
  1964.         continue;
  1965.     }
  1966.     
  1967.     /*
  1968.      * Now we're down to a level-0 node that contains a line that contains
  1969.      * a relevant tag transition.  Set up line information and go back to
  1970.      * the beginning of the loop to search through lines.
  1971.      */
  1972.  
  1973.     searchPtr->curIndex.linePtr = nodePtr->children.linePtr;
  1974.     searchPtr->curIndex.charIndex = 0;
  1975.     segPtr = searchPtr->curIndex.linePtr->segPtr;
  1976.     if (searchPtr->linesLeft <= 0) {
  1977.         goto searchOver;
  1978.     }
  1979.     continue;
  1980.     }
  1981.  
  1982.     searchOver:
  1983.     searchPtr->linesLeft = 0;
  1984.     searchPtr->segPtr = NULL;
  1985.     return 0;
  1986. }
  1987.  
  1988. /*
  1989.  *----------------------------------------------------------------------
  1990.  *
  1991.  * TkBTreePrevTag --
  1992.  *
  1993.  *    Once a tag search has begun, successive calls to this procedure
  1994.  *    return successive tag toggles in the reverse direction.
  1995.  *    Note:  it is NOT SAFE to call this
  1996.  *    procedure if characters have been inserted into or deleted from
  1997.  *    the B-tree since the call to TkBTreeStartSearch.
  1998.  *
  1999.  * Results:
  2000.  *    The return value is 1 if another toggle was found that met the
  2001.  *    criteria specified in the call to TkBTreeStartSearch;  in this
  2002.  *    case searchPtr->curIndex gives the toggle's position and
  2003.  *    searchPtr->curTagPtr points to its segment.  0 is returned if
  2004.  *    no more matching tag transitions were found; in this case
  2005.  *    searchPtr->curIndex is the same as searchPtr->stopIndex.
  2006.  *
  2007.  * Side effects:
  2008.  *    Information in *searchPtr is modified to update the state of the
  2009.  *    search and indicate where the next tag toggle is located.
  2010.  *
  2011.  *----------------------------------------------------------------------
  2012.  */
  2013.  
  2014. int
  2015. TkBTreePrevTag(searchPtr)
  2016.     register TkTextSearch *searchPtr;    /* Information about search in
  2017.                      * progress;  must have been set up by
  2018.                      * call to TkBTreeStartSearch. */
  2019. {
  2020.     register TkTextSegment *segPtr, *prevPtr;
  2021.     register TkTextLine *linePtr, *prevLinePtr;
  2022.     register Node *nodePtr, *node2Ptr, *prevNodePtr;
  2023.     register Summary *summaryPtr;
  2024.     int charIndex;
  2025.     int pastLast;            /* Saw last marker during scan */
  2026.     int linesSkipped;
  2027.  
  2028.     if (searchPtr->linesLeft <= 0) {
  2029.     goto searchOver;
  2030.     }
  2031.  
  2032.     /*
  2033.      * The outermost loop iterates over lines that may potentially contain
  2034.      * a relevant tag transition, starting from the current segment in
  2035.      * the current line.  "nextPtr" is maintained as the last segment in
  2036.      * a line that we can look at. 
  2037.      */
  2038.  
  2039.     while (1) {
  2040.     /*
  2041.      * Check for the last toggle before the current segment on this line.
  2042.      */
  2043.     charIndex = 0;
  2044.     if (searchPtr->lastPtr == NULL) {
  2045.         /* 
  2046.          * Search back to the very beginning, so pastLast is irrelevent.
  2047.          */
  2048.         pastLast = 1; 
  2049.     } else {
  2050.         pastLast = 0;
  2051.     }
  2052.     for (prevPtr = NULL, segPtr = searchPtr->curIndex.linePtr->segPtr ;
  2053.         segPtr != NULL && segPtr != searchPtr->nextPtr;
  2054.         segPtr = segPtr->nextPtr) {
  2055.         if (((segPtr->typePtr == &tkTextToggleOnType)
  2056.             || (segPtr->typePtr == &tkTextToggleOffType))
  2057.             && (searchPtr->allTags
  2058.             || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
  2059.         prevPtr = segPtr;
  2060.         searchPtr->curIndex.charIndex = charIndex;
  2061.         }
  2062.         if (segPtr == searchPtr->lastPtr) {
  2063.             prevPtr = NULL;   /* Segments earlier than last don't count */
  2064.         pastLast = 1;
  2065.         }
  2066.         charIndex += segPtr->size;
  2067.     }
  2068.     if (prevPtr != NULL) {
  2069.         if (searchPtr->linesLeft == 1 && !pastLast) {
  2070.         /*
  2071.          * We found a segment that is before the stopping index.
  2072.          * Note that it is OK if prevPtr == lastPtr.
  2073.          */
  2074.         goto searchOver;
  2075.         }
  2076.         searchPtr->segPtr = prevPtr;
  2077.         searchPtr->nextPtr = prevPtr;
  2078.         searchPtr->tagPtr = prevPtr->body.toggle.tagPtr;
  2079.         return 1;
  2080.     }
  2081.     
  2082.     searchPtr->linesLeft--;
  2083.     if (searchPtr->linesLeft <= 0) {
  2084.         goto searchOver;
  2085.     }
  2086.  
  2087.     /*
  2088.      * See if there are more lines associated with the current parent
  2089.      * node.  If so, go back to the top of the loop to search the previous
  2090.      * one.
  2091.      */
  2092.  
  2093.     nodePtr = searchPtr->curIndex.linePtr->parentPtr;
  2094.     for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  2095.         linePtr != NULL && linePtr != searchPtr->curIndex.linePtr;
  2096.         prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
  2097.         /* empty loop body */ ;
  2098.     }
  2099.     if (prevLinePtr != NULL) {
  2100.         searchPtr->curIndex.linePtr = prevLinePtr;
  2101.         searchPtr->nextPtr = NULL;
  2102.         continue;
  2103.     }
  2104.     if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
  2105.         goto searchOver;
  2106.     }
  2107.     
  2108.     /*
  2109.      * Search across and up through the B-tree's node hierarchy looking
  2110.      * for the previous node that has a relevant tag transition somewhere in
  2111.      * its subtree.  The search and line counting is trickier with/out
  2112.      * back pointers. We'll scan all the nodes under a parent up to
  2113.      * the current node, searching all of them for tag state.  The last
  2114.      * one we find, if any, is recorded in prevNodePtr, and any nodes
  2115.      * past prevNodePtr that don't have tag state increment linesSkipped.
  2116.      */
  2117.     
  2118.     while (1) {
  2119.         for (prevNodePtr = NULL, linesSkipped = 0,
  2120.             node2Ptr = nodePtr->parentPtr->children.nodePtr ;
  2121.             node2Ptr != nodePtr;  node2Ptr = node2Ptr->nextPtr) {
  2122.         for (summaryPtr = node2Ptr->summaryPtr; summaryPtr != NULL;
  2123.             summaryPtr = summaryPtr->nextPtr) {
  2124.             if ((searchPtr->allTags) ||
  2125.                 (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  2126.             prevNodePtr = node2Ptr;
  2127.             linesSkipped = 0;
  2128.             goto keepLooking;
  2129.             }
  2130.         }
  2131.         linesSkipped += node2Ptr->numLines;
  2132.  
  2133.         keepLooking:
  2134.         continue;
  2135.         }
  2136.         if (prevNodePtr != NULL) {
  2137.         nodePtr = prevNodePtr;
  2138.         searchPtr->linesLeft -= linesSkipped;
  2139.         goto gotNodeWithTag;
  2140.         }
  2141.         nodePtr = nodePtr->parentPtr;
  2142.         if (nodePtr->parentPtr == NULL ||
  2143.         nodePtr == searchPtr->tagPtr->tagRootPtr) {
  2144.         goto searchOver;
  2145.         }
  2146.     }
  2147.     
  2148.     /*
  2149.      * At this point we've found a subtree that has a relevant tag
  2150.      * transition.  Now search down (and across) through that subtree
  2151.      * to find the last level-0 node that has a relevant tag transition.
  2152.      */
  2153.     
  2154.     gotNodeWithTag:
  2155.     while (nodePtr->level > 0) {
  2156.         for (linesSkipped = 0, prevNodePtr = NULL,
  2157.             nodePtr = nodePtr->children.nodePtr; nodePtr != NULL ;
  2158.             nodePtr = nodePtr->nextPtr) {
  2159.         for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2160.             summaryPtr = summaryPtr->nextPtr) {
  2161.             if ((searchPtr->allTags)
  2162.                 || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  2163.             prevNodePtr = nodePtr;
  2164.             linesSkipped = 0;
  2165.             goto keepLooking2;
  2166.             }
  2167.         }
  2168.         linesSkipped += nodePtr->numLines;
  2169.  
  2170.         keepLooking2:
  2171.         continue;
  2172.         }
  2173.         if (prevNodePtr == NULL) {
  2174.         panic("TkBTreePrevTag found incorrect tag summary info.");
  2175.         }
  2176.         searchPtr->linesLeft -= linesSkipped;
  2177.         nodePtr = prevNodePtr;
  2178.     }
  2179.     
  2180.     /*
  2181.      * Now we're down to a level-0 node that contains a line that contains
  2182.      * a relevant tag transition.  Set up line information and go back to
  2183.      * the beginning of the loop to search through lines.  We start with
  2184.      * the last line below the node.
  2185.      */
  2186.  
  2187.     for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  2188.         linePtr != NULL ;
  2189.         prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
  2190.         /* empty loop body */ ;
  2191.     }
  2192.     searchPtr->curIndex.linePtr = prevLinePtr;
  2193.     searchPtr->curIndex.charIndex = 0;
  2194.     if (searchPtr->linesLeft <= 0) {
  2195.         goto searchOver;
  2196.     }
  2197.     continue;
  2198.     }
  2199.  
  2200.     searchOver:
  2201.     searchPtr->linesLeft = 0;
  2202.     searchPtr->segPtr = NULL;
  2203.     return 0;
  2204. }
  2205.  
  2206. /*
  2207.  *----------------------------------------------------------------------
  2208.  *
  2209.  * TkBTreeCharTagged --
  2210.  *
  2211.  *    Determine whether a particular character has a particular tag.
  2212.  *
  2213.  * Results:
  2214.  *    The return value is 1 if the given tag is in effect at the
  2215.  *    character given by linePtr and ch, and 0 otherwise.
  2216.  *
  2217.  * Side effects:
  2218.  *    None.
  2219.  *
  2220.  *----------------------------------------------------------------------
  2221.  */
  2222.  
  2223. int
  2224. TkBTreeCharTagged(indexPtr, tagPtr)
  2225.     TkTextIndex *indexPtr;        /* Indicates a character position at
  2226.                      * which to check for a tag. */
  2227.     TkTextTag *tagPtr;            /* Tag of interest. */
  2228. {
  2229.     register Node *nodePtr;
  2230.     register TkTextLine *siblingLinePtr;
  2231.     register TkTextSegment *segPtr;
  2232.     TkTextSegment *toggleSegPtr;
  2233.     int toggles, index;
  2234.  
  2235.     /* 
  2236.      * Check for toggles for the tag in indexPtr's line but before
  2237.      * indexPtr.  If there is one, its type indicates whether or
  2238.      * not the character is tagged.
  2239.      */
  2240.  
  2241.     toggleSegPtr = NULL;
  2242.     for (index = 0, segPtr = indexPtr->linePtr->segPtr;
  2243.         (index + segPtr->size) <= indexPtr->charIndex;
  2244.         index += segPtr->size, segPtr = segPtr->nextPtr) {
  2245.     if (((segPtr->typePtr == &tkTextToggleOnType)
  2246.         || (segPtr->typePtr == &tkTextToggleOffType))
  2247.         && (segPtr->body.toggle.tagPtr == tagPtr)) {
  2248.         toggleSegPtr = segPtr;
  2249.     }
  2250.     }
  2251.     if (toggleSegPtr != NULL) {
  2252.     return (toggleSegPtr->typePtr == &tkTextToggleOnType);
  2253.     }
  2254.  
  2255.     /*
  2256.      * No toggle in this line.  Look for toggles for the tag in lines
  2257.      * that are predecessors of indexPtr->linePtr but under the same
  2258.      * level-0 node.
  2259.      */
  2260.  
  2261.     toggles = 0;
  2262.     for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
  2263.         siblingLinePtr != indexPtr->linePtr;
  2264.         siblingLinePtr = siblingLinePtr->nextPtr) {
  2265.     for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
  2266.         segPtr = segPtr->nextPtr) {
  2267.         if (((segPtr->typePtr == &tkTextToggleOnType)
  2268.             || (segPtr->typePtr == &tkTextToggleOffType))
  2269.             && (segPtr->body.toggle.tagPtr == tagPtr)) {
  2270.         toggleSegPtr = segPtr;
  2271.         }
  2272.     }
  2273.     }
  2274.     if (toggleSegPtr != NULL) {
  2275.     return (toggleSegPtr->typePtr == &tkTextToggleOnType);
  2276.     }
  2277.  
  2278.     /*
  2279.      * No toggle in this node.  Scan upwards through the ancestors of
  2280.      * this node, counting the number of toggles of the given tag in
  2281.      * siblings that precede that node.
  2282.      */
  2283.  
  2284.     toggles = 0;
  2285.     for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
  2286.         nodePtr = nodePtr->parentPtr) {
  2287.     register Node *siblingPtr;
  2288.     register Summary *summaryPtr;
  2289.  
  2290.     for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
  2291.         siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
  2292.         for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
  2293.             summaryPtr = summaryPtr->nextPtr) {
  2294.         if (summaryPtr->tagPtr == tagPtr) {
  2295.             toggles += summaryPtr->toggleCount;
  2296.         }
  2297.         }
  2298.     }
  2299.     if (nodePtr == tagPtr->tagRootPtr) {
  2300.         break;
  2301.     }
  2302.     }
  2303.  
  2304.     /*
  2305.      * An odd number of toggles means that the tag is present at the
  2306.      * given point.
  2307.      */
  2308.  
  2309.     return toggles & 1;
  2310. }
  2311.  
  2312. /*
  2313.  *----------------------------------------------------------------------
  2314.  *
  2315.  * TkBTreeGetTags --
  2316.  *
  2317.  *    Return information about all of the tags that are associated
  2318.  *    with a particular character in a B-tree of text.
  2319.  *
  2320.  * Results:
  2321.  *    The return value is a malloc-ed array containing pointers to
  2322.  *    information for each of the tags that is associated with
  2323.  *    the character at the position given by linePtr and ch.  The
  2324.  *    word at *numTagsPtr is filled in with the number of pointers
  2325.  *    in the array.  It is up to the caller to free the array by
  2326.  *    passing it to free.  If there are no tags at the given character
  2327.  *    then a NULL pointer is returned and *numTagsPtr will be set to 0.
  2328.  *
  2329.  * Side effects:
  2330.  *    None.
  2331.  *
  2332.  *----------------------------------------------------------------------
  2333.  */
  2334.  
  2335.     /* ARGSUSED */
  2336. TkTextTag **
  2337. TkBTreeGetTags(indexPtr, numTagsPtr)
  2338.     TkTextIndex *indexPtr;    /* Indicates a particular position in
  2339.                  * the B-tree. */
  2340.     int *numTagsPtr;        /* Store number of tags found at this
  2341.                  * location. */
  2342. {
  2343.     register Node *nodePtr;
  2344.     register TkTextLine *siblingLinePtr;
  2345.     register TkTextSegment *segPtr;
  2346.     int src, dst, index;
  2347.     TagInfo tagInfo;
  2348. #define NUM_TAG_INFOS 10
  2349.  
  2350.     tagInfo.numTags = 0;
  2351.     tagInfo.arraySize = NUM_TAG_INFOS;
  2352.     tagInfo.tagPtrs = (TkTextTag **) ckalloc((unsigned)
  2353.         NUM_TAG_INFOS*sizeof(TkTextTag *));
  2354.     tagInfo.counts = (int *) ckalloc((unsigned)
  2355.         NUM_TAG_INFOS*sizeof(int));
  2356.  
  2357.     /*
  2358.      * Record tag toggles within the line of indexPtr but preceding
  2359.      * indexPtr.
  2360.      */
  2361.  
  2362.     for (index = 0, segPtr = indexPtr->linePtr->segPtr;
  2363.         (index + segPtr->size) <= indexPtr->charIndex;
  2364.         index += segPtr->size, segPtr = segPtr->nextPtr) {
  2365.     if ((segPtr->typePtr == &tkTextToggleOnType)
  2366.         || (segPtr->typePtr == &tkTextToggleOffType)) {
  2367.         IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
  2368.     }
  2369.     }
  2370.  
  2371.     /*
  2372.      * Record toggles for tags in lines that are predecessors of
  2373.      * indexPtr->linePtr but under the same level-0 node.
  2374.      */
  2375.  
  2376.     for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
  2377.         siblingLinePtr != indexPtr->linePtr;
  2378.         siblingLinePtr = siblingLinePtr->nextPtr) {
  2379.     for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
  2380.         segPtr = segPtr->nextPtr) {
  2381.         if ((segPtr->typePtr == &tkTextToggleOnType)
  2382.             || (segPtr->typePtr == &tkTextToggleOffType)) {
  2383.         IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
  2384.         }
  2385.     }
  2386.     }
  2387.  
  2388.     /*
  2389.      * For each node in the ancestry of this line, record tag toggles
  2390.      * for all siblings that precede that node.
  2391.      */
  2392.  
  2393.     for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
  2394.         nodePtr = nodePtr->parentPtr) {
  2395.     register Node *siblingPtr;
  2396.     register Summary *summaryPtr;
  2397.  
  2398.     for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
  2399.         siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
  2400.         for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
  2401.             summaryPtr = summaryPtr->nextPtr) {
  2402.         if (summaryPtr->toggleCount & 1) {
  2403.             IncCount(summaryPtr->tagPtr, summaryPtr->toggleCount,
  2404.                 &tagInfo);
  2405.         }
  2406.         }
  2407.     }
  2408.     }
  2409.  
  2410.     /*
  2411.      * Go through the tag information and squash out all of the tags
  2412.      * that have even toggle counts (these tags exist before the point
  2413.      * of interest, but not at the desired character itself).
  2414.      */
  2415.  
  2416.     for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
  2417.     if (tagInfo.counts[src] & 1) {
  2418.         tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
  2419.         dst++;
  2420.     }
  2421.     }
  2422.     *numTagsPtr = dst;
  2423.     ckfree((char *) tagInfo.counts);
  2424.     if (dst == 0) {
  2425.     ckfree((char *) tagInfo.tagPtrs);
  2426.     return NULL;
  2427.     }
  2428.     return tagInfo.tagPtrs;
  2429. }
  2430.  
  2431. /*
  2432.  *----------------------------------------------------------------------
  2433.  *
  2434.  * IncCount --
  2435.  *
  2436.  *    This is a utility procedure used by TkBTreeGetTags.  It
  2437.  *    increments the count for a particular tag, adding a new
  2438.  *    entry for that tag if there wasn't one previously.
  2439.  *
  2440.  * Results:
  2441.  *    None.
  2442.  *
  2443.  * Side effects:
  2444.  *    The information at *tagInfoPtr may be modified, and the arrays
  2445.  *    may be reallocated to make them larger.
  2446.  *
  2447.  *----------------------------------------------------------------------
  2448.  */
  2449.  
  2450. static void
  2451. IncCount(tagPtr, inc, tagInfoPtr)
  2452.     TkTextTag *tagPtr;        /* Handle for tag. */
  2453.     int inc;            /* Amount by which to increment tag count. */
  2454.     TagInfo *tagInfoPtr;    /* Holds cumulative information about tags;
  2455.                  * increment count here. */
  2456. {
  2457.     register TkTextTag **tagPtrPtr;
  2458.     int count;
  2459.  
  2460.     for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
  2461.         count > 0; tagPtrPtr++, count--) {
  2462.     if (*tagPtrPtr == tagPtr) {
  2463.         tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
  2464.         return;
  2465.     }
  2466.     }
  2467.  
  2468.     /*
  2469.      * There isn't currently an entry for this tag, so we have to
  2470.      * make a new one.  If the arrays are full, then enlarge the
  2471.      * arrays first.
  2472.      */
  2473.  
  2474.     if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
  2475.     TkTextTag **newTags;
  2476.     int *newCounts, newSize;
  2477.  
  2478.     newSize = 2*tagInfoPtr->arraySize;
  2479.     newTags = (TkTextTag **) ckalloc((unsigned)
  2480.         (newSize*sizeof(TkTextTag *)));
  2481.     memcpy((VOID *) newTags, (VOID *) tagInfoPtr->tagPtrs,
  2482.         tagInfoPtr->arraySize * sizeof(TkTextTag *));
  2483.     ckfree((char *) tagInfoPtr->tagPtrs);
  2484.     tagInfoPtr->tagPtrs = newTags;
  2485.     newCounts = (int *) ckalloc((unsigned) (newSize*sizeof(int)));
  2486.     memcpy((VOID *) newCounts, (VOID *) tagInfoPtr->counts,
  2487.         tagInfoPtr->arraySize * sizeof(int));
  2488.     ckfree((char *) tagInfoPtr->counts);
  2489.     tagInfoPtr->counts = newCounts;
  2490.     tagInfoPtr->arraySize = newSize;
  2491.     }
  2492.  
  2493.     tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
  2494.     tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
  2495.     tagInfoPtr->numTags++;
  2496. }
  2497.  
  2498. /*
  2499.  *----------------------------------------------------------------------
  2500.  *
  2501.  * TkBTreeCheck --
  2502.  *
  2503.  *    This procedure runs a set of consistency checks over a B-tree
  2504.  *    and panics if any inconsistencies are found.
  2505.  *
  2506.  * Results:
  2507.  *    None.
  2508.  *
  2509.  * Side effects:
  2510.  *    If a structural defect is found, the procedure panics with an
  2511.  *    error message.
  2512.  *
  2513.  *----------------------------------------------------------------------
  2514.  */
  2515.  
  2516. void
  2517. TkBTreeCheck(tree)
  2518.     TkTextBTree tree;        /* Tree to check. */
  2519. {
  2520.     BTree *treePtr = (BTree *) tree;
  2521.     register Summary *summaryPtr;
  2522.     register Node *nodePtr;
  2523.     register TkTextLine *linePtr;
  2524.     register TkTextSegment *segPtr;
  2525.     register TkTextTag *tagPtr;
  2526.     Tcl_HashEntry *entryPtr;
  2527.     Tcl_HashSearch search;
  2528.     int count;
  2529.  
  2530.     /*
  2531.      * Make sure that the tag toggle counts and the tag root pointers are OK.
  2532.      */
  2533.     for (entryPtr = Tcl_FirstHashEntry(&treePtr->textPtr->tagTable, &search);
  2534.         entryPtr != NULL ; entryPtr = Tcl_NextHashEntry(&search)) {
  2535.     tagPtr = (TkTextTag *) Tcl_GetHashValue(entryPtr);
  2536.     nodePtr = tagPtr->tagRootPtr;
  2537.     if (nodePtr == (Node *) NULL) {
  2538.         if (tagPtr->toggleCount != 0) {
  2539.         panic("TkBTreeCheck found \"%s\" with toggles (%d) but no root",
  2540.             tagPtr->name, tagPtr->toggleCount);
  2541.         }
  2542.         continue;        /* no ranges for the tag */
  2543.     } else if (tagPtr->toggleCount == 0) {
  2544.         panic("TkBTreeCheck found root for \"%s\" with no toggles",
  2545.             tagPtr->name);
  2546.     } else if (tagPtr->toggleCount & 1) {
  2547.         panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
  2548.             tagPtr->name, tagPtr->toggleCount);
  2549.     }
  2550.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2551.         summaryPtr = summaryPtr->nextPtr) {
  2552.         if (summaryPtr->tagPtr == tagPtr) {
  2553.         panic("TkBTreeCheck found root node with summary info");
  2554.         }
  2555.     }
  2556.     count = 0;
  2557.     if (nodePtr->level > 0) {
  2558.         for (nodePtr = nodePtr->children.nodePtr ; nodePtr != NULL ;
  2559.             nodePtr = nodePtr->nextPtr) {
  2560.         for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2561.             summaryPtr = summaryPtr->nextPtr) {
  2562.             if (summaryPtr->tagPtr == tagPtr) {
  2563.             count += summaryPtr->toggleCount;
  2564.             }
  2565.         }
  2566.         }
  2567.     } else {
  2568.         for (linePtr = nodePtr->children.linePtr ; linePtr != NULL ;
  2569.             linePtr = linePtr->nextPtr) {
  2570.         for (segPtr = linePtr->segPtr; segPtr != NULL;
  2571.             segPtr = segPtr->nextPtr) {
  2572.             if ((segPtr->typePtr == &tkTextToggleOnType ||
  2573.              segPtr->typePtr == &tkTextToggleOffType) &&
  2574.              segPtr->body.toggle.tagPtr == tagPtr) {
  2575.             count++;
  2576.             }
  2577.         }
  2578.         }
  2579.     }
  2580.     if (count != tagPtr->toggleCount) {
  2581.         panic("TkBTreeCheck toggleCount (%d) wrong for \"%s\" should be (%d)",
  2582.         tagPtr->toggleCount, tagPtr->name, count);
  2583.     }
  2584.     }
  2585.  
  2586.     /*
  2587.      * Call a recursive procedure to do the main body of checks.
  2588.      */
  2589.  
  2590.     nodePtr = treePtr->rootPtr;
  2591.     CheckNodeConsistency(treePtr->rootPtr);
  2592.  
  2593.     /*
  2594.      * Make sure that there are at least two lines in the text and
  2595.      * that the last line has no characters except a newline.
  2596.      */
  2597.  
  2598.     if (nodePtr->numLines < 2) {
  2599.     panic("TkBTreeCheck: less than 2 lines in tree");
  2600.     }
  2601.     while (nodePtr->level > 0) {
  2602.     nodePtr = nodePtr->children.nodePtr;
  2603.     while (nodePtr->nextPtr != NULL) {
  2604.         nodePtr = nodePtr->nextPtr;
  2605.     }
  2606.     }
  2607.     linePtr = nodePtr->children.linePtr;
  2608.     while (linePtr->nextPtr != NULL) {
  2609.     linePtr = linePtr->nextPtr;
  2610.     }
  2611.     segPtr = linePtr->segPtr;
  2612.     while ((segPtr->typePtr == &tkTextToggleOffType)
  2613.         || (segPtr->typePtr == &tkTextRightMarkType)
  2614.         || (segPtr->typePtr == &tkTextLeftMarkType)) {
  2615.     /*
  2616.      * It's OK to toggle a tag off in the last line, but
  2617.      * not to start a new range.  It's also OK to have marks
  2618.      * in the last line.
  2619.      */
  2620.  
  2621.     segPtr = segPtr->nextPtr;
  2622.     }
  2623.     if (segPtr->typePtr != &tkTextCharType) {
  2624.     panic("TkBTreeCheck: last line has bogus segment type");
  2625.     }
  2626.     if (segPtr->nextPtr != NULL) {
  2627.     panic("TkBTreeCheck: last line has too many segments");
  2628.     }
  2629.     if (segPtr->size != 1) {
  2630.     panic("TkBTreeCheck: last line has wrong # characters: %d",
  2631.         segPtr->size);
  2632.     }
  2633.     if ((segPtr->body.chars[0] != '\n') || (segPtr->body.chars[1] != 0)) {
  2634.     panic("TkBTreeCheck: last line had bad value: %s",
  2635.         segPtr->body.chars);
  2636.     }
  2637. }
  2638.  
  2639. /*
  2640.  *----------------------------------------------------------------------
  2641.  *
  2642.  * CheckNodeConsistency --
  2643.  *
  2644.  *    This procedure is called as part of consistency checking for
  2645.  *    B-trees:  it checks several aspects of a node and also runs
  2646.  *    checks recursively on the node's children.
  2647.  *
  2648.  * Results:
  2649.  *    None.
  2650.  *
  2651.  * Side effects:
  2652.  *    If anything suspicious is found in the tree structure, the
  2653.  *    procedure panics.
  2654.  *
  2655.  *----------------------------------------------------------------------
  2656.  */
  2657.  
  2658. static void
  2659. CheckNodeConsistency(nodePtr)
  2660.     register Node *nodePtr;        /* Node whose subtree should be
  2661.                      * checked. */
  2662. {
  2663.     register Node *childNodePtr;
  2664.     register Summary *summaryPtr, *summaryPtr2;
  2665.     register TkTextLine *linePtr;
  2666.     register TkTextSegment *segPtr;
  2667.     int numChildren, numLines, toggleCount, minChildren;
  2668.  
  2669.     if (nodePtr->parentPtr != NULL) {
  2670.     minChildren = MIN_CHILDREN;
  2671.     } else if (nodePtr->level > 0) {
  2672.     minChildren = 2;
  2673.     } else  {
  2674.     minChildren = 1;
  2675.     }
  2676.     if ((nodePtr->numChildren < minChildren)
  2677.         || (nodePtr->numChildren > MAX_CHILDREN)) {
  2678.     panic("CheckNodeConsistency: bad child count (%d)",
  2679.         nodePtr->numChildren);
  2680.     }
  2681.  
  2682.     numChildren = 0;
  2683.     numLines = 0;
  2684.     if (nodePtr->level == 0) {
  2685.     for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  2686.         linePtr = linePtr->nextPtr) {
  2687.         if (linePtr->parentPtr != nodePtr) {
  2688.         panic("CheckNodeConsistency: line doesn't point to parent");
  2689.         }
  2690.         if (linePtr->segPtr == NULL) {
  2691.         panic("CheckNodeConsistency: line has no segments");
  2692.         }
  2693.         for (segPtr = linePtr->segPtr; segPtr != NULL;
  2694.             segPtr = segPtr->nextPtr) {
  2695.         if (segPtr->typePtr->checkProc != NULL) {
  2696.             (*segPtr->typePtr->checkProc)(segPtr, linePtr);
  2697.         }
  2698.         if ((segPtr->size == 0) && (!segPtr->typePtr->leftGravity)
  2699.             && (segPtr->nextPtr != NULL)
  2700.             && (segPtr->nextPtr->size == 0)
  2701.             && (segPtr->nextPtr->typePtr->leftGravity)) {
  2702.             panic("CheckNodeConsistency: wrong segment order for gravity");
  2703.         }
  2704.         if ((segPtr->nextPtr == NULL)
  2705.             && (segPtr->typePtr != &tkTextCharType)) {
  2706.             panic("CheckNodeConsistency: line ended with wrong type");
  2707.         }
  2708.         }
  2709.         numChildren++;
  2710.         numLines++;
  2711.     }
  2712.     } else {
  2713.     for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
  2714.         childNodePtr = childNodePtr->nextPtr) {
  2715.         if (childNodePtr->parentPtr != nodePtr) {
  2716.         panic("CheckNodeConsistency: node doesn't point to parent");
  2717.         }
  2718.         if (childNodePtr->level != (nodePtr->level-1)) {
  2719.         panic("CheckNodeConsistency: level mismatch (%d %d)",
  2720.             nodePtr->level, childNodePtr->level);
  2721.         }
  2722.         CheckNodeConsistency(childNodePtr);
  2723.         for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
  2724.             summaryPtr = summaryPtr->nextPtr) {
  2725.         for (summaryPtr2 = nodePtr->summaryPtr; ;
  2726.             summaryPtr2 = summaryPtr2->nextPtr) {
  2727.             if (summaryPtr2 == NULL) {
  2728.             if (summaryPtr->tagPtr->tagRootPtr == nodePtr) {
  2729.                 break;
  2730.             }
  2731.             panic("CheckNodeConsistency: node tag \"%s\" not %s",
  2732.                 summaryPtr->tagPtr->name,
  2733.                 "present in parent summaries");
  2734.             }
  2735.             if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
  2736.             break;
  2737.             }
  2738.         }
  2739.         }
  2740.         numChildren++;
  2741.         numLines += childNodePtr->numLines;
  2742.     }
  2743.     }
  2744.     if (numChildren != nodePtr->numChildren) {
  2745.     panic("CheckNodeConsistency: mismatch in numChildren (%d %d)",
  2746.         numChildren, nodePtr->numChildren);
  2747.     }
  2748.     if (numLines != nodePtr->numLines) {
  2749.     panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
  2750.         numLines, nodePtr->numLines);
  2751.     }
  2752.  
  2753.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2754.         summaryPtr = summaryPtr->nextPtr) {
  2755.     if (summaryPtr->tagPtr->toggleCount == summaryPtr->toggleCount) {
  2756.         panic("CheckNodeConsistency: found unpruned root for \"%s\"",
  2757.         summaryPtr->tagPtr->name);
  2758.     }
  2759.     toggleCount = 0;
  2760.     if (nodePtr->level == 0) {
  2761.         for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  2762.             linePtr = linePtr->nextPtr) {
  2763.         for (segPtr = linePtr->segPtr; segPtr != NULL;
  2764.             segPtr = segPtr->nextPtr) {
  2765.             if ((segPtr->typePtr != &tkTextToggleOnType)
  2766.                 && (segPtr->typePtr != &tkTextToggleOffType)) {
  2767.             continue;
  2768.             }
  2769.             if (segPtr->body.toggle.tagPtr == summaryPtr->tagPtr) {
  2770.             toggleCount ++;
  2771.             }
  2772.         }
  2773.         }
  2774.     } else {
  2775.         for (childNodePtr = nodePtr->children.nodePtr;
  2776.             childNodePtr != NULL;
  2777.             childNodePtr = childNodePtr->nextPtr) {
  2778.         for (summaryPtr2 = childNodePtr->summaryPtr;
  2779.             summaryPtr2 != NULL;
  2780.             summaryPtr2 = summaryPtr2->nextPtr) {
  2781.             if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
  2782.             toggleCount += summaryPtr2->toggleCount;
  2783.             }
  2784.         }
  2785.         }
  2786.     }
  2787.     if (toggleCount != summaryPtr->toggleCount) {
  2788.         panic("CheckNodeConsistency: mismatch in toggleCount (%d %d)",
  2789.             toggleCount, summaryPtr->toggleCount);
  2790.     }
  2791.     for (summaryPtr2 = summaryPtr->nextPtr; summaryPtr2 != NULL;
  2792.         summaryPtr2 = summaryPtr2->nextPtr) {
  2793.         if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
  2794.         panic("CheckNodeConsistency: duplicated node tag: %s",
  2795.             summaryPtr->tagPtr->name);
  2796.         }
  2797.     }
  2798.     }
  2799. }
  2800.  
  2801. /*
  2802.  *----------------------------------------------------------------------
  2803.  *
  2804.  * Rebalance --
  2805.  *
  2806.  *    This procedure is called when a node of a B-tree appears to be
  2807.  *    out of balance (too many children, or too few).  It rebalances
  2808.  *    that node and all of its ancestors in the tree.
  2809.  *
  2810.  * Results:
  2811.  *    None.
  2812.  *
  2813.  * Side effects:
  2814.  *    The internal structure of treePtr may change.
  2815.  *
  2816.  *----------------------------------------------------------------------
  2817.  */
  2818.  
  2819. static void
  2820. Rebalance(treePtr, nodePtr)
  2821.     BTree *treePtr;            /* Tree that is being rebalanced. */
  2822.     register Node *nodePtr;        /* Node that may be out of balance. */
  2823. {
  2824.     /*
  2825.      * Loop over the entire ancestral chain of the node, working up
  2826.      * through the tree one node at a time until the root node has
  2827.      * been processed.
  2828.      */
  2829.  
  2830.     for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
  2831.     register Node *newPtr, *childPtr;
  2832.     register TkTextLine *linePtr;
  2833.     int i;
  2834.  
  2835.     /*
  2836.      * Check to see if the node has too many children.  If it does,
  2837.      * then split off all but the first MIN_CHILDREN into a separate
  2838.      * node following the original one.  Then repeat until the
  2839.      * node has a decent size.
  2840.      */
  2841.  
  2842.     if (nodePtr->numChildren > MAX_CHILDREN) {
  2843.         while (1) {
  2844.         /*
  2845.          * If the node being split is the root node, then make a
  2846.          * new root node above it first.
  2847.          */
  2848.     
  2849.         if (nodePtr->parentPtr == NULL) {
  2850.             newPtr = (Node *) ckalloc(sizeof(Node));
  2851.             newPtr->parentPtr = NULL;
  2852.             newPtr->nextPtr = NULL;
  2853.             newPtr->summaryPtr = NULL;
  2854.             newPtr->level = nodePtr->level + 1;
  2855.             newPtr->children.nodePtr = nodePtr;
  2856.             newPtr->numChildren = 1;
  2857.             newPtr->numLines = nodePtr->numLines;
  2858.             RecomputeNodeCounts(newPtr);
  2859.             treePtr->rootPtr = newPtr;
  2860.         }
  2861.         newPtr = (Node *) ckalloc(sizeof(Node));
  2862.         newPtr->parentPtr = nodePtr->parentPtr;
  2863.         newPtr->nextPtr = nodePtr->nextPtr;
  2864.         nodePtr->nextPtr = newPtr;
  2865.         newPtr->summaryPtr = NULL;
  2866.         newPtr->level = nodePtr->level;
  2867.         newPtr->numChildren = nodePtr->numChildren - MIN_CHILDREN;
  2868.         if (nodePtr->level == 0) {
  2869.             for (i = MIN_CHILDREN-1,
  2870.                 linePtr = nodePtr->children.linePtr;
  2871.                 i > 0; i--, linePtr = linePtr->nextPtr) {
  2872.             /* Empty loop body. */
  2873.             }
  2874.             newPtr->children.linePtr = linePtr->nextPtr;
  2875.             linePtr->nextPtr = NULL;
  2876.         } else {
  2877.             for (i = MIN_CHILDREN-1,
  2878.                 childPtr = nodePtr->children.nodePtr;
  2879.                 i > 0; i--, childPtr = childPtr->nextPtr) {
  2880.             /* Empty loop body. */
  2881.             }
  2882.             newPtr->children.nodePtr = childPtr->nextPtr;
  2883.             childPtr->nextPtr = NULL;
  2884.         }
  2885.         RecomputeNodeCounts(nodePtr);
  2886.         nodePtr->parentPtr->numChildren++;
  2887.         nodePtr = newPtr;
  2888.         if (nodePtr->numChildren <= MAX_CHILDREN) {
  2889.             RecomputeNodeCounts(nodePtr);
  2890.             break;
  2891.         }
  2892.         }
  2893.     }
  2894.  
  2895.     while (nodePtr->numChildren < MIN_CHILDREN) {
  2896.         register Node *otherPtr;
  2897.         Node *halfwayNodePtr = NULL;    /* Initialization needed only */
  2898.         TkTextLine *halfwayLinePtr = NULL;    /* to prevent cc warnings. */
  2899.         int totalChildren, firstChildren, i;
  2900.  
  2901.         /*
  2902.          * Too few children for this node.  If this is the root then,
  2903.          * it's OK for it to have less than MIN_CHILDREN children
  2904.          * as long as it's got at least two.  If it has only one
  2905.          * (and isn't at level 0), then chop the root node out of
  2906.          * the tree and use its child as the new root.
  2907.          */
  2908.  
  2909.         if (nodePtr->parentPtr == NULL) {
  2910.         if ((nodePtr->numChildren == 1) && (nodePtr->level > 0)) {
  2911.             treePtr->rootPtr = nodePtr->children.nodePtr;
  2912.             treePtr->rootPtr->parentPtr = NULL;
  2913.             DeleteSummaries(nodePtr->summaryPtr);
  2914.             ckfree((char *) nodePtr);
  2915.         }
  2916.         return;
  2917.         }
  2918.  
  2919.         /*
  2920.          * Not the root.  Make sure that there are siblings to
  2921.          * balance with.
  2922.          */
  2923.  
  2924.         if (nodePtr->parentPtr->numChildren < 2) {
  2925.         Rebalance(treePtr, nodePtr->parentPtr);
  2926.         continue;
  2927.         }
  2928.  
  2929.         /*
  2930.          * Find a sibling neighbor to borrow from, and arrange for
  2931.          * nodePtr to be the earlier of the pair.
  2932.          */
  2933.  
  2934.         if (nodePtr->nextPtr == NULL) {
  2935.         for (otherPtr = nodePtr->parentPtr->children.nodePtr;
  2936.             otherPtr->nextPtr != nodePtr;
  2937.             otherPtr = otherPtr->nextPtr) {
  2938.             /* Empty loop body. */
  2939.         }
  2940.         nodePtr = otherPtr;
  2941.         }
  2942.         otherPtr = nodePtr->nextPtr;
  2943.  
  2944.         /*
  2945.          * We're going to either merge the two siblings together
  2946.          * into one node or redivide the children among them to
  2947.          * balance their loads.  As preparation, join their two
  2948.          * child lists into a single list and remember the half-way
  2949.          * point in the list.
  2950.          */
  2951.  
  2952.         totalChildren = nodePtr->numChildren + otherPtr->numChildren;
  2953.         firstChildren = totalChildren/2;
  2954.         if (nodePtr->children.nodePtr == NULL) {
  2955.         nodePtr->children = otherPtr->children;
  2956.         otherPtr->children.nodePtr = NULL;
  2957.         otherPtr->children.linePtr = NULL;
  2958.         }
  2959.         if (nodePtr->level == 0) {
  2960.         register TkTextLine *linePtr;
  2961.  
  2962.         for (linePtr = nodePtr->children.linePtr, i = 1;
  2963.             linePtr->nextPtr != NULL;
  2964.             linePtr = linePtr->nextPtr, i++) {
  2965.             if (i == firstChildren) {
  2966.             halfwayLinePtr = linePtr;
  2967.             }
  2968.         }
  2969.         linePtr->nextPtr = otherPtr->children.linePtr;
  2970.         while (i <= firstChildren) {
  2971.             halfwayLinePtr = linePtr;
  2972.             linePtr = linePtr->nextPtr;
  2973.             i++;
  2974.         }
  2975.         } else {
  2976.         register Node *childPtr;
  2977.  
  2978.         for (childPtr = nodePtr->children.nodePtr, i = 1;
  2979.             childPtr->nextPtr != NULL;
  2980.             childPtr = childPtr->nextPtr, i++) {
  2981.             if (i <= firstChildren) {
  2982.             if (i == firstChildren) {
  2983.                 halfwayNodePtr = childPtr;
  2984.             }
  2985.             }
  2986.         }
  2987.         childPtr->nextPtr = otherPtr->children.nodePtr;
  2988.         while (i <= firstChildren) {
  2989.             halfwayNodePtr = childPtr;
  2990.             childPtr = childPtr->nextPtr;
  2991.             i++;
  2992.         }
  2993.         }
  2994.  
  2995.         /*
  2996.          * If the two siblings can simply be merged together, do it.
  2997.          */
  2998.  
  2999.         if (totalChildren <= MAX_CHILDREN) {
  3000.         RecomputeNodeCounts(nodePtr);
  3001.         nodePtr->nextPtr = otherPtr->nextPtr;
  3002.         nodePtr->parentPtr->numChildren--;
  3003.         DeleteSummaries(otherPtr->summaryPtr);
  3004.         ckfree((char *) otherPtr);
  3005.         continue;
  3006.         }
  3007.  
  3008.         /*
  3009.          * The siblings can't be merged, so just divide their
  3010.          * children evenly between them.
  3011.          */
  3012.  
  3013.         if (nodePtr->level == 0) {
  3014.         otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
  3015.         halfwayLinePtr->nextPtr = NULL;
  3016.         } else {
  3017.         otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
  3018.         halfwayNodePtr->nextPtr = NULL;
  3019.         }
  3020.         RecomputeNodeCounts(nodePtr);
  3021.         RecomputeNodeCounts(otherPtr);
  3022.     }
  3023.     }
  3024. }
  3025.  
  3026. /*
  3027.  *----------------------------------------------------------------------
  3028.  *
  3029.  * RecomputeNodeCounts --
  3030.  *
  3031.  *    This procedure is called to recompute all the counts in a node
  3032.  *    (tags, child information, etc.) by scanning the information in
  3033.  *    its descendants.  This procedure is called during rebalancing
  3034.  *    when a node's child structure has changed.
  3035.  *
  3036.  * Results:
  3037.  *    None.
  3038.  *
  3039.  * Side effects:
  3040.  *    The tag counts for nodePtr are modified to reflect its current
  3041.  *    child structure, as are its numChildren and numLines fields.
  3042.  *    Also, all of the childrens' parentPtr fields are made to point
  3043.  *    to nodePtr.
  3044.  *
  3045.  *----------------------------------------------------------------------
  3046.  */
  3047.  
  3048. static void
  3049. RecomputeNodeCounts(nodePtr)
  3050.     register Node *nodePtr;        /* Node whose tag summary information
  3051.                      * must be recomputed. */
  3052. {
  3053.     register Summary *summaryPtr, *summaryPtr2;
  3054.     register Node *childPtr;
  3055.     register TkTextLine *linePtr;
  3056.     register TkTextSegment *segPtr;
  3057.     TkTextTag *tagPtr;
  3058.  
  3059.     /*
  3060.      * Zero out all the existing counts for the node, but don't delete
  3061.      * the existing Summary records (most of them will probably be reused).
  3062.      */
  3063.  
  3064.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  3065.         summaryPtr = summaryPtr->nextPtr) {
  3066.     summaryPtr->toggleCount = 0;
  3067.     }
  3068.     nodePtr->numChildren = 0;
  3069.     nodePtr->numLines = 0;
  3070.  
  3071.     /*
  3072.      * Scan through the children, adding the childrens' tag counts into
  3073.      * the node's tag counts and adding new Summary structures if
  3074.      * necessary.
  3075.      */
  3076.  
  3077.     if (nodePtr->level == 0) {
  3078.     for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  3079.         linePtr = linePtr->nextPtr) {
  3080.         nodePtr->numChildren++;
  3081.         nodePtr->numLines++;
  3082.         linePtr->parentPtr = nodePtr;
  3083.         for (segPtr = linePtr->segPtr; segPtr != NULL;
  3084.             segPtr = segPtr->nextPtr) {
  3085.         if (((segPtr->typePtr != &tkTextToggleOnType)
  3086.             && (segPtr->typePtr != &tkTextToggleOffType))
  3087.             || !(segPtr->body.toggle.inNodeCounts)) {
  3088.             continue;
  3089.         }
  3090.         tagPtr = segPtr->body.toggle.tagPtr;
  3091.         for (summaryPtr = nodePtr->summaryPtr; ;
  3092.             summaryPtr = summaryPtr->nextPtr) {
  3093.             if (summaryPtr == NULL) {
  3094.             summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  3095.             summaryPtr->tagPtr = tagPtr;
  3096.             summaryPtr->toggleCount = 1;
  3097.             summaryPtr->nextPtr = nodePtr->summaryPtr;
  3098.             nodePtr->summaryPtr = summaryPtr;
  3099.             break;
  3100.             }
  3101.             if (summaryPtr->tagPtr == tagPtr) {
  3102.             summaryPtr->toggleCount++;
  3103.             break;
  3104.             }
  3105.         }
  3106.         }
  3107.     }
  3108.     } else {
  3109.     for (childPtr = nodePtr->children.nodePtr; childPtr != NULL;
  3110.         childPtr = childPtr->nextPtr) {
  3111.         nodePtr->numChildren++;
  3112.         nodePtr->numLines += childPtr->numLines;
  3113.         childPtr->parentPtr = nodePtr;
  3114.         for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
  3115.             summaryPtr2 = summaryPtr2->nextPtr) {
  3116.         for (summaryPtr = nodePtr->summaryPtr; ;
  3117.             summaryPtr = summaryPtr->nextPtr) {
  3118.             if (summaryPtr == NULL) {
  3119.             summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  3120.             summaryPtr->tagPtr = summaryPtr2->tagPtr;
  3121.             summaryPtr->toggleCount = summaryPtr2->toggleCount;
  3122.             summaryPtr->nextPtr = nodePtr->summaryPtr;
  3123.             nodePtr->summaryPtr = summaryPtr;
  3124.             break;
  3125.             }
  3126.             if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
  3127.             summaryPtr->toggleCount += summaryPtr2->toggleCount;
  3128.             break;
  3129.             }
  3130.         }
  3131.         }
  3132.     }
  3133.     }
  3134.  
  3135.     /*
  3136.      * Scan through the node's tag records again and delete any Summary
  3137.      * records that still have a zero count, or that have all the toggles.
  3138.      * The node with the children that account for all the tags toggles
  3139.      * have no summary information, and they become the tagRootPtr for the tag.
  3140.      */
  3141.  
  3142.     summaryPtr2 = NULL;
  3143.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
  3144.     if (summaryPtr->toggleCount > 0 && 
  3145.         summaryPtr->toggleCount < summaryPtr->tagPtr->toggleCount) {
  3146.         if (nodePtr->level == summaryPtr->tagPtr->tagRootPtr->level) {
  3147.         /*
  3148.          * The tag's root node split and some toggles left.
  3149.          * The tag root must move up a level.
  3150.          */
  3151.         summaryPtr->tagPtr->tagRootPtr = nodePtr->parentPtr;
  3152.         }
  3153.         summaryPtr2 = summaryPtr;
  3154.         summaryPtr = summaryPtr->nextPtr;
  3155.         continue;
  3156.     }
  3157.     if (summaryPtr->toggleCount == summaryPtr->tagPtr->toggleCount) {
  3158.         /*
  3159.          * A node merge has collected all the toggles under one node.
  3160.          * Push the root down to this level.
  3161.          */
  3162.         summaryPtr->tagPtr->tagRootPtr = nodePtr;
  3163.     }
  3164.     if (summaryPtr2 != NULL) {
  3165.         summaryPtr2->nextPtr = summaryPtr->nextPtr;
  3166.         ckfree((char *) summaryPtr);
  3167.         summaryPtr = summaryPtr2->nextPtr;
  3168.     } else {
  3169.         nodePtr->summaryPtr = summaryPtr->nextPtr;
  3170.         ckfree((char *) summaryPtr);
  3171.         summaryPtr = nodePtr->summaryPtr;
  3172.     }
  3173.     }
  3174. }
  3175.  
  3176. /*
  3177.  *----------------------------------------------------------------------
  3178.  *
  3179.  * TkBTreeNumLines --
  3180.  *
  3181.  *    This procedure returns a count of the number of lines of
  3182.  *    text present in a given B-tree.
  3183.  *
  3184.  * Results:
  3185.  *    The return value is a count of the number of usable lines
  3186.  *    in tree (i.e. it doesn't include the dummy line that is just
  3187.  *     used to mark the end of the tree).
  3188.  *
  3189.  * Side effects:
  3190.  *    None.
  3191.  *
  3192.  *----------------------------------------------------------------------
  3193.  */
  3194.  
  3195. int
  3196. TkBTreeNumLines(tree)
  3197.     TkTextBTree tree;            /* Information about tree. */
  3198. {
  3199.     BTree *treePtr = (BTree *) tree;
  3200.     return treePtr->rootPtr->numLines - 1;
  3201. }
  3202.  
  3203. /*
  3204.  *--------------------------------------------------------------
  3205.  *
  3206.  * CharSplitProc --
  3207.  *
  3208.  *    This procedure implements splitting for character segments.
  3209.  *
  3210.  * Results:
  3211.  *    The return value is a pointer to a chain of two segments
  3212.  *    that have the same characters as segPtr except split
  3213.  *    among the two segments.
  3214.  *
  3215.  * Side effects:
  3216.  *    Storage for segPtr is freed.
  3217.  *
  3218.  *--------------------------------------------------------------
  3219.  */
  3220.  
  3221. static TkTextSegment *
  3222. CharSplitProc(segPtr, index)
  3223.     TkTextSegment *segPtr;        /* Pointer to segment to split. */
  3224.     int index;                /* Position within segment at which
  3225.                      * to split. */
  3226. {
  3227.     TkTextSegment *newPtr1, *newPtr2;
  3228.  
  3229.     newPtr1 = (TkTextSegment *) ckalloc(CSEG_SIZE(index));
  3230.     newPtr2 = (TkTextSegment *) ckalloc(
  3231.         CSEG_SIZE(segPtr->size - index));
  3232.     newPtr1->typePtr = &tkTextCharType;
  3233.     newPtr1->nextPtr = newPtr2;
  3234.     newPtr1->size = index;
  3235.     strncpy(newPtr1->body.chars, segPtr->body.chars, (size_t) index);
  3236.     newPtr1->body.chars[index] = 0;
  3237.     newPtr2->typePtr = &tkTextCharType;
  3238.     newPtr2->nextPtr = segPtr->nextPtr;
  3239.     newPtr2->size = segPtr->size - index;
  3240.     strcpy(newPtr2->body.chars, segPtr->body.chars + index);
  3241.     ckfree((char*) segPtr);
  3242.     return newPtr1;
  3243. }
  3244.  
  3245. /*
  3246.  *--------------------------------------------------------------
  3247.  *
  3248.  * CharCleanupProc --
  3249.  *
  3250.  *    This procedure merges adjacent character segments into
  3251.  *    a single character segment, if possible.
  3252.  *
  3253.  * Results:
  3254.  *    The return value is a pointer to the first segment in
  3255.  *    the (new) list of segments that used to start with segPtr.
  3256.  *
  3257.  * Side effects:
  3258.  *    Storage for the segments may be allocated and freed.
  3259.  *
  3260.  *--------------------------------------------------------------
  3261.  */
  3262.  
  3263.     /* ARGSUSED */
  3264. static TkTextSegment *
  3265. CharCleanupProc(segPtr, linePtr)
  3266.     TkTextSegment *segPtr;        /* Pointer to first of two adjacent
  3267.                      * segments to join. */
  3268.     TkTextLine *linePtr;        /* Line containing segments (not
  3269.                      * used). */
  3270. {
  3271.     TkTextSegment *segPtr2, *newPtr;
  3272.  
  3273.     segPtr2 = segPtr->nextPtr;
  3274.     if ((segPtr2 == NULL) || (segPtr2->typePtr != &tkTextCharType)) {
  3275.     return segPtr;
  3276.     }
  3277.     newPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(
  3278.         segPtr->size + segPtr2->size));
  3279.     newPtr->typePtr = &tkTextCharType;
  3280.     newPtr->nextPtr = segPtr2->nextPtr;
  3281.     newPtr->size = segPtr->size + segPtr2->size;
  3282.     strcpy(newPtr->body.chars, segPtr->body.chars);
  3283.     strcpy(newPtr->body.chars + segPtr->size, segPtr2->body.chars);
  3284.     ckfree((char*) segPtr);
  3285.     ckfree((char*) segPtr2);
  3286.     return newPtr;
  3287. }
  3288.  
  3289. /*
  3290.  *--------------------------------------------------------------
  3291.  *
  3292.  * CharDeleteProc --
  3293.  *
  3294.  *    This procedure is invoked to delete a character segment.
  3295.  *
  3296.  * Results:
  3297.  *    Always returns 0 to indicate that the segment was deleted.
  3298.  *
  3299.  * Side effects:
  3300.  *    Storage for the segment is freed.
  3301.  *
  3302.  *--------------------------------------------------------------
  3303.  */
  3304.  
  3305.     /* ARGSUSED */
  3306. static int
  3307. CharDeleteProc(segPtr, linePtr, treeGone)
  3308.     TkTextSegment *segPtr;        /* Segment to delete. */
  3309.     TkTextLine *linePtr;        /* Line containing segment. */
  3310.     int treeGone;            /* Non-zero means the entire tree is
  3311.                      * being deleted, so everything must
  3312.                      * get cleaned up. */
  3313. {
  3314.     ckfree((char*) segPtr);
  3315.     return 0;
  3316. }
  3317.  
  3318. /*
  3319.  *--------------------------------------------------------------
  3320.  *
  3321.  * CharCheckProc --
  3322.  *
  3323.  *    This procedure is invoked to perform consistency checks
  3324.  *    on character segments.
  3325.  *
  3326.  * Results:
  3327.  *    None.
  3328.  *
  3329.  * Side effects:
  3330.  *    If the segment isn't inconsistent then the procedure
  3331.  *    panics.
  3332.  *
  3333.  *--------------------------------------------------------------
  3334.  */
  3335.  
  3336.     /* ARGSUSED */
  3337. static void
  3338. CharCheckProc(segPtr, linePtr)
  3339.     TkTextSegment *segPtr;        /* Segment to check. */
  3340.     TkTextLine *linePtr;        /* Line containing segment. */
  3341. {
  3342.     /*
  3343.      * Make sure that the segment contains the number of
  3344.      * characters indicated by its header, and that the last
  3345.      * segment in a line ends in a newline.  Also make sure
  3346.      * that there aren't ever two character segments adjacent
  3347.      * to each other:  they should be merged together.
  3348.      */
  3349.  
  3350.     if (segPtr->size <= 0) {
  3351.     panic("CharCheckProc: segment has size <= 0");
  3352.     }
  3353.     if (strlen(segPtr->body.chars) != segPtr->size) {
  3354.     panic("CharCheckProc: segment has wrong size");
  3355.     }
  3356.     if (segPtr->nextPtr == NULL) {
  3357.     if (segPtr->body.chars[segPtr->size-1] != '\n') {
  3358.         panic("CharCheckProc: line doesn't end with newline");
  3359.     }
  3360.     } else {
  3361.     if (segPtr->nextPtr->typePtr == &tkTextCharType) {
  3362.         panic("CharCheckProc: adjacent character segments weren't merged");
  3363.     }
  3364.     }
  3365. }
  3366.  
  3367. /*
  3368.  *--------------------------------------------------------------
  3369.  *
  3370.  * ToggleDeleteProc --
  3371.  *
  3372.  *    This procedure is invoked to delete toggle segments.
  3373.  *
  3374.  * Results:
  3375.  *    Returns 1 to indicate that the segment may not be deleted,
  3376.  *    unless the entire B-tree is going away.
  3377.  *
  3378.  * Side effects:
  3379.  *    If the tree is going away then the toggle's memory is
  3380.  *    freed;  otherwise the toggle counts in nodes above the
  3381.  *    segment get updated.
  3382.  *
  3383.  *--------------------------------------------------------------
  3384.  */
  3385.  
  3386. static int
  3387. ToggleDeleteProc(segPtr, linePtr, treeGone)
  3388.     TkTextSegment *segPtr;        /* Segment to check. */
  3389.     TkTextLine *linePtr;        /* Line containing segment. */
  3390.     int treeGone;            /* Non-zero means the entire tree is
  3391.                      * being deleted, so everything must
  3392.                      * get cleaned up. */
  3393. {
  3394.     if (treeGone) {
  3395.     ckfree((char *) segPtr);
  3396.     return 0;
  3397.     }
  3398.  
  3399.     /*
  3400.      * This toggle is in the middle of a range of characters that's
  3401.      * being deleted.  Refuse to die.  We'll be moved to the end of
  3402.      * the deleted range and our cleanup procedure will be called
  3403.      * later.  Decrement node toggle counts here, and set a flag
  3404.      * so we'll re-increment them in the cleanup procedure.
  3405.      */
  3406.  
  3407.     if (segPtr->body.toggle.inNodeCounts) {
  3408.     ChangeNodeToggleCount(linePtr->parentPtr,
  3409.         segPtr->body.toggle.tagPtr, -1);
  3410.     segPtr->body.toggle.inNodeCounts = 0;
  3411.     }
  3412.     return 1;
  3413. }
  3414.  
  3415. /*
  3416.  *--------------------------------------------------------------
  3417.  *
  3418.  * ToggleCleanupProc --
  3419.  *
  3420.  *    This procedure is called when a toggle is part of a line that's
  3421.  *    been modified in some way.  It's invoked after the
  3422.  *    modifications are complete.
  3423.  *
  3424.  * Results:
  3425.  *    The return value is the head segment in a new list
  3426.  *    that is to replace the tail of the line that used to
  3427.  *    start at segPtr.  This allows the procedure to delete
  3428.  *    or modify segPtr.
  3429.  *
  3430.  * Side effects:
  3431.  *    Toggle counts in the nodes above the new line will be
  3432.  *    updated if they're not already.  Toggles may be collapsed
  3433.  *    if there are duplicate toggles at the same position.
  3434.  *
  3435.  *--------------------------------------------------------------
  3436.  */
  3437.  
  3438. static TkTextSegment *
  3439. ToggleCleanupProc(segPtr, linePtr)
  3440.     TkTextSegment *segPtr;    /* Segment to check. */
  3441.     TkTextLine *linePtr;    /* Line that now contains segment. */
  3442. {
  3443.     TkTextSegment *segPtr2, *prevPtr;
  3444.     int counts;
  3445.  
  3446.     /*
  3447.      * If this is a toggle-off segment, look ahead through the next
  3448.      * segments to see if there's a toggle-on segment for the same tag
  3449.      * before any segments with non-zero size.  If so then the two
  3450.      * toggles cancel each other;  remove them both.
  3451.      */
  3452.  
  3453.     if (segPtr->typePtr == &tkTextToggleOffType) {
  3454.     for (prevPtr = segPtr, segPtr2 = prevPtr->nextPtr;
  3455.         (segPtr2 != NULL) && (segPtr2->size == 0);
  3456.         prevPtr = segPtr2, segPtr2 = prevPtr->nextPtr) {
  3457.         if (segPtr2->typePtr != &tkTextToggleOnType) {
  3458.         continue;
  3459.         }
  3460.         if (segPtr2->body.toggle.tagPtr != segPtr->body.toggle.tagPtr) {
  3461.         continue;
  3462.         }
  3463.         counts = segPtr->body.toggle.inNodeCounts
  3464.             + segPtr2->body.toggle.inNodeCounts;
  3465.         if (counts != 0) {
  3466.         ChangeNodeToggleCount(linePtr->parentPtr,
  3467.             segPtr->body.toggle.tagPtr, -counts);
  3468.         }
  3469.         prevPtr->nextPtr = segPtr2->nextPtr;
  3470.         ckfree((char *) segPtr2);
  3471.         segPtr2 = segPtr->nextPtr;
  3472.         ckfree((char *) segPtr);
  3473.         return segPtr2;
  3474.     }
  3475.     }
  3476.  
  3477.     if (!segPtr->body.toggle.inNodeCounts) {
  3478.     ChangeNodeToggleCount(linePtr->parentPtr,
  3479.         segPtr->body.toggle.tagPtr, 1);
  3480.     segPtr->body.toggle.inNodeCounts = 1;
  3481.     }
  3482.     return segPtr;
  3483. }
  3484.  
  3485. /*
  3486.  *--------------------------------------------------------------
  3487.  *
  3488.  * ToggleLineChangeProc --
  3489.  *
  3490.  *    This procedure is invoked when a toggle segment is about
  3491.  *    to move from one line to another.
  3492.  *
  3493.  * Results:
  3494.  *    None.
  3495.  *
  3496.  * Side effects:
  3497.  *    Toggle counts are decremented in the nodes above the line.
  3498.  *
  3499.  *--------------------------------------------------------------
  3500.  */
  3501.  
  3502. static void
  3503. ToggleLineChangeProc(segPtr, linePtr)
  3504.     TkTextSegment *segPtr;    /* Segment to check. */
  3505.     TkTextLine *linePtr;    /* Line that used to contain segment. */
  3506. {
  3507.     if (segPtr->body.toggle.inNodeCounts) {
  3508.     ChangeNodeToggleCount(linePtr->parentPtr,
  3509.         segPtr->body.toggle.tagPtr, -1);
  3510.     segPtr->body.toggle.inNodeCounts = 0;
  3511.     }
  3512. }
  3513.  
  3514. /*
  3515.  *--------------------------------------------------------------
  3516.  *
  3517.  * ToggleCheckProc --
  3518.  *
  3519.  *    This procedure is invoked to perform consistency checks
  3520.  *    on toggle segments.
  3521.  *
  3522.  * Results:
  3523.  *    None.
  3524.  *
  3525.  * Side effects:
  3526.  *    If a consistency problem is found the procedure panics.
  3527.  *
  3528.  *--------------------------------------------------------------
  3529.  */
  3530.  
  3531. static void
  3532. ToggleCheckProc(segPtr, linePtr)
  3533.     TkTextSegment *segPtr;        /* Segment to check. */
  3534.     TkTextLine *linePtr;        /* Line containing segment. */
  3535. {
  3536.     register Summary *summaryPtr;
  3537.     int needSummary;
  3538.  
  3539.     if (segPtr->size != 0) {
  3540.     panic("ToggleCheckProc: segment had non-zero size");
  3541.     }
  3542.     if (!segPtr->body.toggle.inNodeCounts) {
  3543.     panic("ToggleCheckProc: toggle counts not updated in nodes");
  3544.     }
  3545.     needSummary = (segPtr->body.toggle.tagPtr->tagRootPtr != linePtr->parentPtr);
  3546.     for (summaryPtr = linePtr->parentPtr->summaryPtr; ;
  3547.         summaryPtr = summaryPtr->nextPtr) {
  3548.     if (summaryPtr == NULL) {
  3549.         if (needSummary) {
  3550.         panic("ToggleCheckProc: tag not present in node");
  3551.         } else {
  3552.         break;
  3553.         }
  3554.     }
  3555.     if (summaryPtr->tagPtr == segPtr->body.toggle.tagPtr) {
  3556.         if (!needSummary) {
  3557.         panic("ToggleCheckProc: tag present in root node summary");
  3558.         }
  3559.         break;
  3560.     }
  3561.     }
  3562. }
  3563.  
  3564. /*
  3565.  *----------------------------------------------------------------------
  3566.  *
  3567.  * TkBTreeCharsInLine --
  3568.  *
  3569.  *    This procedure returns a count of the number of characters
  3570.  *    in a given line.
  3571.  *
  3572.  * Results:
  3573.  *    The return value is the character count for linePtr.
  3574.  *
  3575.  * Side effects:
  3576.  *    None.
  3577.  *
  3578.  *----------------------------------------------------------------------
  3579.  */
  3580.  
  3581. int
  3582. TkBTreeCharsInLine(linePtr)
  3583.     TkTextLine *linePtr;        /* Line whose characters should be
  3584.                      * counted. */
  3585. {
  3586.     TkTextSegment *segPtr;
  3587.     int count;
  3588.  
  3589.     count = 0;
  3590.     for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
  3591.     count += segPtr->size;
  3592.     }
  3593.     return count;
  3594. }
  3595.